Cron-Jobs in Docker - der richtige Ansatz

Cron-Jobs in Docker - der richtige Ansatz

Wiederkehrende Aufgaben in Docker-Umgebungen lösen viele falsch – ein Cron-Daemon im App-Container ist fast immer die schlechteste Wahl.

Anti-Pattern: Cron im App-Container

Ein Container soll einen Prozess ausführen. Zwei Prozesse (App + crond) in einem Container zu betreiben erzeugt Probleme: Logs werden gemischt, Signals werden nicht sauber weitergeleitet, Restarts betreffen immer beide Prozesse.

# SCHLECHT – nicht so machen
FROM python:3.12
RUN apt-get install -y cron
COPY mycron /etc/cron.d/mycron
CMD service cron start && python app.py  # Zwei Prozesse, keine saubere Trennung

Option 1: Host-Cron (einfach und zuverlässig)

Der Host-Cron ruft docker exec oder docker run auf. Keine zusätzlichen Container, funktioniert immer.

# crontab -e auf dem Host
# Täglich um 2 Uhr: Datenbankbackup im laufenden Container
0 2 * * * docker exec mydb mysqldump -u root -psecret mydb > /backups/db-$(date +\%Y\%m\%d).sql

# Alle 5 Minuten: einmaliger Task-Container
*/5 * * * * docker run --rm --network myapp_default myimage python /app/tasks/cleanup.py

Vorteile: Einfach, direkte Logs per syslog, kein weiterer Container.

Option 2: Supercronic

Supercronic ist ein cron-Ersatz der speziell für Container entwickelt wurde – ein einzelner Prozess, saubere Logs zu stdout/stderr, keine root-Rechte nötig.

FROM alpine:3.19

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

COPY crontab /crontab
CMD ["/usr/local/bin/supercronic", "/crontab"]
# /crontab
*/5 * * * * python /app/tasks/cleanup.py
0 3 * * * python /app/tasks/backup.py

Supercronic ist die beste Wahl wenn der Container selbst der Cron-Container sein soll.

Option 3: Ofelia

Ofelia ist ein dedizierten Docker-Cron-Manager der andere Container via Labels steuert:

services:
  ofelia:
    image: mcuadros/ofelia:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - app

  app:
    image: myapp
    labels:
      ofelia.enabled: "true"
      ofelia.job-exec.cleanup.schedule: "@every 5m"
      ofelia.job-exec.cleanup.command: "python /app/tasks/cleanup.py"
      ofelia.job-exec.backup.schedule: "0 3 * * *"
      ofelia.job-exec.backup.command: "python /app/tasks/backup.py"

Ofelia liest die Labels und führt die Befehle im App-Container aus. Logs landen im Ofelia-Container, übersichtlich getrennt.

Option 4: Compose mit restart: on-failure

Für einfache einmalige Tasks die regelmäßig als neuer Container gestartet werden – kombiniert mit einem externen Trigger:

services:
  backup-job:
    image: myimage
    command: python /app/tasks/backup.py
    restart: "no"
    profiles:
      - jobs
# Vom Host aus starten
docker compose run --rm backup-job

Empfehlung

| Szenario | Lösung |
|---|---|
| Einfache Tasks auf einem Server | Host-Cron |
| Container der nur Cron-Tasks ausführt | Supercronic |
| Mehrere Services mit verschiedenen Tasks | Ofelia |
| Einmalige parametrisierbare Jobs | docker compose run |

Auf dem eigenen Server ist Host-Cron die unkomplizierteste Wahl. Für containerisierte Umgebungen ohne Host-Zugriff (managed Platforms) ist Supercronic ideal.