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)