Drupal Worker
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 cron —
0 2 * * *, ranges, lists and
steps. - Shortcuts —
@daily,@hourly,
@weeklyand 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, sincephp:scriptis a Drush command. The shell runner hands
the whole line tosh -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.
- Add a Server for each machine that will run a worker, with
the hostname that machine reports (hostnameon the box, or
drush druker:jobswill show you what it detected). Set how often
its worker should re-read the schedule. - Add Cron Jobs. Each is a Drush command — without the
leadingdrush— and a schedule.cronis a perfectly
reasonable first job. - 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. - Build and start the worker on the server. Build it into
the project root — the directory holdingvendor/— 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 runningdrush 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.
Recommended modules/libraries
- 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
Releases
| Version | Type | Core | Notes | Release date | |
|---|---|---|---|---|---|
| 1.x-dev | Dev | 11 | Sep 13, 2026 |