docker-cron runs cron-like jobs inside already running Docker containers based
on container labels.
The controller uses the Docker Engine API through /var/run/docker.sock. The
runtime logic is a Python standard-library script.
labels:
cron.<jobname>.schedule: "<cron expression>"
cron.<jobname>.jitterSeconds: "<max jitter in seconds>"
cron.<jobname>.timeoutSeconds: "<max execution wait in seconds>"
cron.<jobname>.command: "<command>"Required labels:
cron.<jobname>.schedulecron.<jobname>.command
Optional labels:
cron.<jobname>.jitterSeconds, defaults to0cron.<jobname>.timeoutSeconds, defaults toJOB_TIMEOUT_SECONDS
Example:
services:
wordpress:
image: bitnami/wordpress
labels:
cron.wp.schedule: "*/5 * * * *"
cron.wp.jitterSeconds: "60"
cron.wp.timeoutSeconds: "300"
cron.wp.command: "wp --path=/bitnami/wordpress cron event run --due-now"Job names may not contain dots.
The scheduler accepts five-field cron expressions:
minute hour day-of-month month day-of-week
Supported forms:
**/51,2,310-2010-20/2- month aliases like
jan,feb - weekday aliases like
sun,mon
When both day-of-month and day-of-week are restricted, the job runs when either field matches, matching traditional cron behavior.
services:
docker-cron:
image: ghcr.io/lxix/docker-cron:latest
restart: unless-stopped
environment:
TZ: Europe/Budapest
DISCOVERY_INTERVAL_SECONDS: "60"
JOB_TIMEOUT_SECONDS: "3600"
MAX_CONCURRENT_JOBS: "10"
MAX_JITTER_SECONDS: "3600"
OUTPUT_LIMIT_BYTES: "4096"
LOG_JOB_OUTPUT: "true"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:roBuild locally:
docker build -t docker-cron:latest .Run the test suite with the project's coverage gate:
python3 tools/check_coverage.pyThis uses only Python's standard library and enforces 100% line coverage for
docker_cron.py.
- On startup, the controller scans all currently running containers.
- It watches Docker container events and rescans after
start,stop,die,kill, anddestroyevents. - It also performs a full periodic rescan, controlled by
DISCOVERY_INTERVAL_SECONDS. - Jobs do not overlap with a previous still-running execution of the same job.
- The controller enforces a global
MAX_CONCURRENT_JOBSlimit. - A job that exceeds its timeout is logged as timed out. The controller keeps that job slot reserved until Docker reports that the exec process has finished, or the target container stops, so timed-out jobs do not pile up with overlapping retries. Use command-level timeouts too when hard process cancellation matters.
- Job output is captured up to
OUTPUT_LIMIT_BYTESand marked as truncated when the command writes more. SetLOG_JOB_OUTPUT=falseto omit stdout/stderr from controller logs. - Commands are executed inside the labeled container with:
/bin/sh -lc "<command>"If the target image has no /bin/sh, the controller retries the command as a
direct executable invocation parsed with shell-like quoting rules. For example,
/whoami --help becomes ["/whoami", "--help"].
Environment variables:
TZ: timezone used for schedule evaluation, defaultUTCDISCOVERY_INTERVAL_SECONDS: periodic full rescan interval, default60DOCKER_SOCKET: Docker Unix socket path, default/var/run/docker.sockDOCKER_TIMEOUT_SECONDS: Docker API request timeout, default30JOB_TIMEOUT_SECONDS: default and maximum per-job execution wait timeout, default3600MAX_CONCURRENT_JOBS: maximum concurrently running job workers, default10MAX_JITTER_SECONDS: maximum accepted per-job jitter, default3600OUTPUT_LIMIT_BYTES: maximum captured job output bytes, default4096LOG_JOB_OUTPUT: include captured stdout/stderr in logs, defaulttrueSELF_CONTAINER_ID: optional explicit id/prefix for the controller container
Mounting /var/run/docker.sock grants the controller root-equivalent control
over the Docker host, even when the bind mount uses :ro. The read-only bind
mount protects the socket path from filesystem writes inside the controller
container, but it does not make the Docker Engine API read-only. Treat container
labels as a trusted control plane: any actor that can create or change cron.*
labels can make the controller execute commands inside those containers. Only
run this on trusted hosts, with trusted compose files or deploy manifests, and
avoid using it across isolation boundaries where untrusted users can create
containers.
Use mcuadros/ofelia when you need a
broader Docker scheduler with extra job types, INI configuration, remote
Docker options, or built-in reporting integrations.