Skip to main content
Drupal is a registered trademark of Dries Buytaert
Release: GoogleTag Manager 2.0.3 Minor update available for module gtm (2.0.3). Release: User Reference with Invite 1.4.1 Minor update available for module user_reference_invite (1.4.1). Release: General Data Protection Regulation Compliance 2.0.2 Minor update available for module gdpr_compliance (2.0.2). Release: Contact Mail 2.0.5 Minor update available for module contact_mail (2.0.5). Release: Contact ajax 3.0.3 Minor update available for module contact_ajax (3.0.3). Release: Commerce ML Starter 2.0.3 Minor update available for module cmlstarter (2.0.3). Release: Commerce ML Migrations 2.0.3 Minor update available for module cmlmigrations (2.0.3). Release: Commerce ML Exchange 2.0.3 Minor update available for module cmlexchange (2.0.3). Usage Milestone: Term CSV Export Import Module term_csv_export_import crossed 1,000 active installs. Module Revived: Prototype: Select 1.2.2 Module prototype_select updated after 10 months of inactivity (1.2.2).

Druker moves a server's cron schedule out of crontab and into
Drupal, where it can be edited in the admin UI, reviewed like any other
configuration, and seen by the people who actually want to know what runs. A
small Go worker sits beside the site, asks Drupal what this machine should be
running, and runs it.

With Druker, the schedule is content and configuration in Drupal, the worker
picks up changes on its next refresh, and adding a job is a form.

Features

Two entity types hold the schedule:

  • A Server is a machine that runs a worker, matched to it by
    hostname, with its own refresh interval.
  • A Cron Job is a Drush command plus when to run it. Assign
    it to a server, or leave the server empty to run it everywhere.

What you can say about "when":

  • Standard cron0 2 * * *, ranges, lists and
    steps.
  • Shortcuts@daily, @hourly,
    @weekly and the rest.
  • Plain English — "every 5 minutes", "every 3 hours", "every
    week". Resolved to cron before the worker ever sees it.
  • A date and time, for a job that should run once and never
    again — a data migration, a scheduled maintenance window. Completions are
    remembered, so restarting the worker does not run it a second time.

And what you can say about how:

  • Dependencies. A job can be set to run after another. It
    waits while that one is running and starts when it finishes.
  • Asynchronous or not. An asynchronous job starts alongside
    anything else due in the same minute; one that is not holds the rest of that
    minute behind it.
  • No job ever overlaps itself. A job still running when its
    next turn comes round is skipped, with a warning. Two copies of a queue
    processor is how a queue gets worked twice and an email gets sent twice.
  • Drush or a shell. Drush is the default and covers PHP
    too, since php:script is a Drush command. The shell runner hands
    the whole line to sh -c, so pipes, redirection and any binary on
    the machine work — for backups, syncs and the rest. Shell jobs are off unless
    the site opts in with $settings['druker_allow_shell'] = TRUE;,
    because that turns the Druker permission from "run any Drush command" into
    "run anything on this machine", and that is a decision for whoever owns the
    server rather than whoever administers the site.
  • Per-server schedules. Four web servers behind a load
    balancer, and only one of them should be processing the mail queue: that is a
    field on the job, not a difference between four crontabs.

The worker is one static Go binary with no runtime dependencies. It logs one
structured line per event to stdout, which is what a container platform wants;
stops cleanly on SIGTERM, giving running jobs time to finish; kills
a job's whole process group when a timeout is reached, so a command that spawns
children cannot leave them behind; and has a -dry-run flag that
prints exactly what it would run and exits non-zero if anything in the schedule
is unreadable — which makes it usable as a deployment check.

Jobs that are computed rather than stored can be added from code by
subscribing to an event, so a module can contribute its own schedule without
anyone creating entities for it.

Post-Installation

Enable the module, then work at Configuration › System › Druker,
across three tabs: Dashboard, Cron Jobs and Servers. There is no new content
type to place and nothing to add to a text format.

The Dashboard is the page worth knowing about. It answers
what the two lists cannot — what each machine is actually going to do — with a
card per server showing the jobs its worker will receive, and a grid of the day
hour by hour, which is where you notice that everything you own runs at three
in the morning. It leads with what will not run: a schedule the worker
cannot read, a shell job on a site that has not enabled them, a job assigned to
a server that is disabled or gone.

  1. Add a Server for each machine that will run a worker, with
    the hostname that machine reports (hostname on the box, or
    drush druker:jobs will show you what it detected). Set how often
    its worker should re-read the schedule.
  2. Add Cron Jobs. Each is a Drush command — without the
    leading drush — and a schedule. cron is a perfectly
    reasonable first job.
  3. Check it with drush druker:check, which tests
    every job the way the worker will and exits non-zero if any of them would be
    dropped.
  4. Build and start the worker on the server. Build it into
    the project root — the directory holding vendor/ — and it needs
    no arguments at all:
    go build -o druker ./worker &&
      ./druker

    . Under systemd, which starts services in /, pass
    -drush /path/to/vendor/bin/drush; the README has a unit file to
    copy. Jobs run in the project root rather than wherever the worker was
    launched, so a relative path in a command always means the same thing.

Before starting it for real, ./druker -dry-run prints the
schedule as the worker understands it. That is also the fastest way to confirm
the server's hostname matches the Server you created — if it does not, the
worker gets the jobs that are assigned to no server, and nothing else.

Two things worth knowing. Jobs run as the user the worker runs as, so run it
as the same user your site does. And a one-time job's completion is written to a
small state file: in a container, point -state at a volume, or a
one-time job will run again after every deploy.

Administer Druker jobs and servers is a restricted permission. A job
is a command line, so granting it is granting command execution as the web
user.

Additional Requirements

  • Drupal 11.
  • Drush, on any server running the worker. The worker asks
    for its schedule by running drush druker:jobs, and the jobs it runs
    are Drush commands.
  • Go 1.23 or later, to build the worker — only on the machine
    that builds it. The result is a static binary with no runtime dependencies, and
    the worker itself uses nothing beyond the Go standard library, deliberately: it
    ships to machines that may have no route to a module proxy.

No other Drupal modules, and no PHP libraries. The cron parsing is the
worker's own and the shorthand resolution on the Drupal side is this module's
own, so there is no scheduling library on either half.

  • Advanced Queue — complementary rather than overlapping. It
    gives you queues worth processing; Druker is what schedules the processor, on
    the server you choose.
  • Klaxon
    — alerting from the same author, and the natural other half of a scheduler.
    Druker will run your jobs; Klaxon is what tells you when one of them stopped
    running, through its "cron is falling behind" and "queue backlog" alerts. It
    works the other way too: Klaxon's own scheduled alerts normally ride on Drupal
    cron, and giving them a Druker job instead means they keep firing even when a
    cron run is wedged.

Similar projects

  • Ultimate Cron schedules Drupal's own hook_cron
    implementations, giving each one its own rule and locking. It is excellent at
    that, and it still runs inside a Drupal cron request. Druker schedules arbitrary
    Drush commands from a process outside the site, so a long import is not a web
    request and does not share a PHP timeout with one.
  • Automated Cron (core) runs cron on the back of a page
    request. Fine for a small site; it means your schedule depends on somebody
    visiting, and it cannot run a job on one server out of four.
  • Scheduler publishes and unpublishes content on a date. A
    different job entirely, and the two coexist happily.
  • A plain crontab is the honest comparison, and it works. What
    it does not give you is a schedule the site can show you, a per-server
    assignment that is a field rather than four files, a change that takes effect
    without a deploy, or an audit of who added what.

Druker does not replace hook_cron. Drupal's cron stays exactly
what it is — and becomes one of the jobs on the schedule.

Supporting this Module

Issues and patches are welcome in the issue queue. There is no funding link.

Community Documentation

The README in the module covers the JSON contract between Drupal and the worker, the worker's flags, and how to add jobs from code.

Depends on

Dependencies of the latest stable release

No dependencies recorded for this project.

Required by

Tracked projects that depend on this one

No tracked projects depend on this one yet.

Activity

Tracked releases
1
Tracked since
Sep 2026
Latest release
6 hours ago
Releases (12 mo)
1 ▲ from 0
Maintenance
Active

Releases

Version Type Core Release date
1.x-dev Dev 11 Sep 13, 2026