DEV Community

Cover image for How to safely run cron jobs in Docker (with monitoring)
Łukasz Maśląg for CronMonitor

Posted on Edited on

How to safely run cron jobs in Docker (with monitoring)

``Have you encountered the following issues when running tasks using the system cron in Decoder?:

  • Logs disappear into thin air
  • Tasks fail at 3 a.m.
  • Environment variables don't work
  • Container doesn't shut down properly.

Problem with system cron

The cron daemon was designed for traditional Linux servers, not containers:

`dockerfile

❌ Don't do this

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y cron
COPY mycron /etc/cron.d/mycron
RUN crontab /etc/cron.d/mycron
CMD cron -f
`

What goes wrong:

  1. No logs - cron writes to syslog, not stdout
  2. Bad signals - SIGTERM doesn't stop jobs gracefully
  3. ENV hell - cron doesn't inherit Docker ENV variables
  4. PID 1 issues - if cron is PID 1, signal handling breaks

The Solution: Supercronic

Supercronic is cron reimagined for containers:
`dockerfile

✅ Do this instead

FROM php:8.2-cli

ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.29/supercronic-linux-amd64 \
SUPERCRONIC=supercronic-linux-amd64 \
SUPERCRONIC_SHA1SUM=cd48d45c4b10f3f0bfdd3a57d054cd05ac96812b

RUN curl -fsSLO "$SUPERCRONIC_URL" \
&& echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \
&& chmod +x "$SUPERCRONIC" \
&& mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
&& ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic

COPY crontab /app/crontab
CMD ["supercronic", "/app/crontab"]
`

The syntax in supercronic remains the same as in crontab:
`bash

/app/crontab

*/15 * * * * php /app/bin/console app:import-data
0 2 * * * /app/scripts/backup.sh
0 */4 * * * php /app/bin/console cache:clear
`

Benefits:

  • ✅ Logs go to stdout (docker logs works!)
  • ✅ Graceful shutdown on SIGTERM
  • ✅ Inherits ENV from Docker automatically
  • ✅ Single process, no daemon complexity

Sample Symfony application running in a container:

Dockerfile:
`dockerfile
FROM php:8.2-cli

Install Supercronic

ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.29/supercronic-linux-amd64
RUN curl -fsSLO "$SUPERCRONIC_URL" \
&& chmod +x supercronic-linux-amd64 \
&& mv supercronic-linux-amd64 /usr/local/bin/supercronic \
&& apt-get update && apt-get install -y git curl unzip

Install Composer

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

Application

COPY . /app
WORKDIR /app
RUN composer install --no-dev --optimize-autoloader

COPY docker/crontab /app/crontab
CMD ["supercronic", "/app/crontab"]
`

docker/crontab:
`bash

Process message queue

  • * * * * php /app/bin/console messenger:consume async --time-limit=3600

Send scheduled emails

*/5 * * * * php /app/bin/console app:send-emails

Generate daily reports

0 6 * * * php /app/bin/console app:generate-reports

Cleanup old data

0 3 * * * php /app/bin/console cache:clear
`

docker-compose.yml:
`yaml
version: '3.8'

services:
app:
build: .
# ... your app service

cron:
build: .
container_name: app-cron
restart: unless-stopped
env_file: .env
depends_on:
- db
- redis
networks:
- app-network
`

Missing Element: Monitoring

Now the cron jobs are working, but how do we find out if and when they failed?

The job fails at 3 a.m. → We'll probably find out when we need to restore a database backup, and cron hasn't executed a script in two weeks :( → Data loss, angry users, a bad day.

Solution: HTTP pings

I created CronMonitor.app specifically for this purpose:
`bash

Add ping after command

*/15 * * * * php /app/your-command.php && curl -fsS https://cronmonitor.app/ping/{token}
`

What you get:

  • Slack/Discord/email alerts when tasks aren't completed correctly
  • Dashboard displaying all tasks
  • Duration and error tracking
  • No agents, no complexity

Example of a complete multi-container solution:

Example of monitoring tasks across multiple containers in a production environment

`yaml
version: '3.8'

services:
# Main application
app:
image: myapp:latest
# ...

# Cron worker for background jobs
cron-worker:
build:
context: .
dockerfile: docker/Dockerfile.cron
container_name: myapp-cron
restart: unless-stopped
env_file: .env
depends_on:
- db
- redis
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
`

docker/Dockerfile.cron:
`dockerfile
FROM php:8.2-cli

Supercronic

RUN curl -L https://github.com/aptible/supercronic/releases/download/v0.2.29/supercronic-linux-amd64 \
-o /usr/local/bin/supercronic && chmod +x /usr/local/bin/supercronic

App dependencies

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN apt-get update && apt-get install -y git curl unzip libzip-dev \
&& docker-php-ext-install pdo_mysql zip

Application

COPY composer.* /app/
WORKDIR /app
RUN composer install --no-dev --no-scripts --optimize-autoloader

COPY . /app
RUN composer dump-autoload --optimize

COPY docker/crontab /app/crontab
CMD ["supercronic", "-debug", "/app/crontab"]
`

docker/crontab with monitoring:
`bash

Queue processor - critical, monitor closely

Hourly data sync

0 * * * * curl -fsS https://cronmonitor.app/ping/{token} && php bin/console app:sync-data || curl -fsS https://cronmonitor.io/fail/sync-def456

Daily backup - MUST succeed

0 2 * * * curl -fsS https://cronmonitor.app/ping/{token} && /app/scripts/backup.sh

Weekly cleanup

0 3 * * 0 curl -fsS https://cronmonitor.app/ping/{token} && php bin/console app:cleanup
`

Debugging Tips

`bash

View logs in real-time

docker logs -f myapp-cron

Test individual commands

docker exec myapp-cron php bin/console app:your-command

Check environment variables

docker exec myapp-cron env | grep DATABASE

Manual crontab test

docker exec myapp-cron supercronic -test /app/crontab
`

Migration Checklist

Moving from standard cron to this setup:

  • [ ] Replace cron with Supercronic in Dockerfile
  • [ ] Move crontab to separate file
  • [ ] Test ENV variables are accessible
  • [ ] Add monitoring pings to jobs
  • [ ] Update docker-compose with health checks
  • [ ] Set up resource limits+
  • [ ] Test graceful shutdown
  • [ ] Document your cron jobs!

Conclusion

After years of fighting with cron in Docker:

  1. Use Supercronic - it's designed for containers
  2. Monitor your jobs - silent failures are the worst
  3. Keep it simple - HTTP pings > complex monitoring

This stack has been running in production for months across multiple projects. Zero silent failures, clear logs, easy debugging.

Full documentation and examples:

If you have any questions, leave a comment!

--

  • I created CronMonitor.app after dealing with silent cron failures too many times. It's free for small projects, and I use it for both personal projects and strictly business applications.**What exactly:*
  • Slack/Discord/email alerts when tasks haven't completed correctly
  • Dashboard displaying all tasks
  • Duration and error tracking

Top comments (0)