Monitoring cron jobs in Docker containers
Running scheduled jobs in containers trips people up in a few well-known ways:
- The cron daemon isn't running. A container runs one main process. If that's your app, nothing
starts
cron, so the crontab is never read. - Environment variables are missing. Cron starts jobs with a minimal environment, so variables
passed with
docker run -eor Compose often aren't visible to the job. - Output disappears. Cron output goes to local mail or nowhere, not to
docker logs. - The container was replaced. A redeploy from an image without the crontab silently drops the schedule.
All four look the same from outside: the job just stops. A heartbeat catches every one of them, because it alerts when the expected ping doesn't arrive.
Option 1: schedule on the host, run in a container
Often the most robust choice: keep cron on the host (or use systemd timers, a Kubernetes CronJob, or your platform's scheduler), and start a one-off container per run. Wrap it with cronpup-run:
0 3 * * * cronpup-run https://cronpup.com/p/BACKUP-UUID docker run --rm --env-file /etc/backup.env backup-image
Option 2: a dedicated cron container
If cron runs inside a container, make it the main process so the container dies, and gets restarted, if
cron dies. For example, with Debian's cron: CMD ["cron", "-f"]. Then ping from each job line:
0 3 * * * root /app/backup.sh > /proc/1/fd/1 2>&1; curl -fsS -m 10 https://cronpup.com/p/BACKUP-UUID/$?
Files in /etc/cron.d need the user column (root above). Redirecting to
/proc/1/fd/1 sends output to docker logs. Ending with /$? reports the exit code,
so a failing job alerts right away instead of only when it's late. curl must be installed in the image.
Environment variables
Write the variables a job needs to a file when the container starts, and source it in the job, rather than relying on cron to pass them through. Never bake secrets into the image.
Monitor your container jobs. 10 monitors are free.