How to safely run cron jobs in Docker (with monitoring)
Source: Dev.to

Problem with system cron
The cron daemon was designed for traditional Linux servers, not containers:
# ❌ 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
- No logs – cron writes to syslog, not
stdout. - Bad signals –
SIGTERMdoesn’t stop jobs gracefully. - ENV hell – cron doesn’t inherit Docker
ENVvariables. - PID 1 issues – if cron is PID 1, signal handling breaks.
The Solution: Supercronic
Supercronic is cron re‑imagined for containers:
# ✅ 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"]
Supercronic crontab syntax
# /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 logsworks). - ✅ Graceful shutdown on
SIGTERM. - ✅ Inherits Docker
ENVautomatically. - ✅ Single process, no daemon complexity.
Sample Symfony application running in a container
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
# 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
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
Cron jobs may fail silently. Without monitoring you won’t know when a job didn’t run, leading to data loss or downtime.
Solution: HTTP pings
Use a service like CronMonitor.app to ping after each 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 extra complexity.
Example of a complete multi‑container solution
docker-compose.yml
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
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)
# Queue processor - critical, monitor closely
* * * * * curl -fsS https://cronmonitor.app/ping/{token} && php bin/console messenger:consume --time-limit=3600
# 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
# 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
- Replace the system
crondaemon with Supercronic. - Move crontab files into the container and reference them in the
CMD. - Ensure all required environment variables are set in the Dockerfile or
docker‑composefile. - Add HTTP ping calls (or another monitoring hook) to each job.
- Verify logs appear via
docker logs. - Test graceful shutdown (
docker stop) and confirm jobs receiveSIGTERM. - Set up alerts in CronMonitor (or your preferred monitoring service).