intermediate 13 min read

How to Set Up Cron Jobs in Docker Containers

Learn how to run cron jobs in Docker containers using system crontab, Supercronic, and Ofelia. Covers environment variables, logging, and Docker Compose examples.

Prerequisites

  • Docker 20+
  • Basic Dockerfile knowledge
  • Understanding of cron syntax

Why Cron in Docker Is Tricky

Running cron in Docker containers presents unique challenges:

  • Cron runs as a separate daemon that doesn't inherit your container's environment variables
  • Log output goes to the cron daemon, not to docker logs
  • Cron expects a full init system, but Docker containers typically run a single process
  • Signal handling is different — docker stop may not gracefully stop cron jobs

There are three main approaches, each with trade-offs.

Approach 1: System Crontab in Container

The simplest but least Docker-native approach:

dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY . .
RUN pip install -r requirements.txt

# Install cron and set up the crontab
RUN apt-get update && apt-get install -y cron && rm -rf /var/lib/apt/lists/*
COPY crontab /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron && crontab /etc/cron.d/my-cron

# Forward cron logs to Docker logs
CMD ["cron", "-f"]
# crontab file
*/5 * * * * cd /app && python task.py >> /proc/1/fd/1 2>&1

Key: Redirect output to /proc/1/fd/1 (PID 1's stdout) so logs appear in docker logs.

Approach 2: Supercronic (Recommended)

Supercronic is a cron replacement designed for containers. It runs in the foreground, logs to stdout/stderr, and handles signals properly.

dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY . .
RUN pip install -r requirements.txt

# Install Supercronic
ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.33/supercronic-linux-amd64
RUN curl -fsSL "$SUPERCRONIC_URL" -o /usr/local/bin/supercronic \
    && chmod +x /usr/local/bin/supercronic

COPY crontab /app/crontab

CMD ["supercronic", "/app/crontab"]
# crontab
*/5 * * * * cd /app && python task.py
0 0 * * * cd /app && python cleanup.py

Advantages over system cron:

  • Logs directly to stdout/stderr (works with docker logs)
  • Handles SIGTERM for graceful shutdown
  • No init system required
  • Passes environment variables to jobs

Approach 3: Ofelia (Docker-Native Scheduler)

Ofelia is a Docker job scheduler that runs as a separate container and triggers jobs in other containers:

yaml
# docker-compose.yml
services:
  ofelia:
    image: mcuadros/ofelia:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      ofelia.enabled: "true"

  app:
    build: .
    labels:
      ofelia.enabled: "true"
      ofelia.job-exec.cleanup.schedule: "0 */5 * * * *"
      ofelia.job-exec.cleanup.command: "python cleanup.py"

Ofelia executes commands inside running containers using Docker exec, keeping your app container clean.

Environment Variables in Docker Cron

System cron doesn't inherit Docker environment variables. Two solutions:

1. Export env vars to a file:

dockerfile
CMD printenv > /etc/environment && cron -f
# crontab
*/5 * * * * . /etc/environment; cd /app && python task.py >> /proc/1/fd/1 2>&1

2. Use an entrypoint script:

bash
#!/bin/bash
# entrypoint.sh
printenv | grep -v "no_proxy" >> /etc/environment
exec "$@"

With Supercronic, this is not an issue — it passes the container's environment to all jobs automatically.

Docker Compose Example

A common pattern: run your web app and cron worker as separate services from the same image:

yaml
services:
  web:
    build: .
    command: gunicorn app:app --bind 0.0.0.0:8000
    ports:
      - "8000:8000"
    env_file: .env

  cron:
    build: .
    command: supercronic /app/crontab
    env_file: .env
    depends_on:
      - db
      - redis

  db:
    image: postgres:16
  redis:
    image: redis:7

Both web and cron share the same Dockerfile but run different commands.

Common Cron Expressions

Standard 5-field expressions:

  • */5 * * * * — Every 5 minutes
  • 0 * * * * — Every hour
  • 0 0 * * * — Daily at midnight
  • 0 2 * * * — Daily at 2 AM (common for cleanup tasks)
  • 0 0 * * 0 — Every Sunday at midnight

Every weekday at 9:00 AM

Next runs (UTC):

Mon, May 18, 2026 09:00

Tue, May 19, 2026 09:00

Wed, May 20, 2026 09:00

Production Tips

Health checks:

dockerfile
HEALTHCHECK --interval=60s --timeout=10s \
  CMD test -f /tmp/cron-heartbeat && \
      find /tmp/cron-heartbeat -mmin -5 | grep -q .

Have your most frequent cron job touch /tmp/cron-heartbeat on success.

Resource limits:

yaml
# docker-compose.yml
cron:
  deploy:
    resources:
      limits:
        cpus: '0.5'
        memory: 256M

Graceful shutdown: Supercronic handles SIGTERM and waits for running jobs to complete. With system cron, jobs are killed immediately on docker stop.

Frequently Asked Questions