Ein nächtliches Datenbank-Backup steht auf 30 2 * * * – und am letzten Sonntag im März existiert kein einziges Backup-Archiv, während das Skript Ende Oktober zweimal hintereinander anläuft. Oder du trägst 0 9 13 * 5 ein, um einen Prüfjob nur an einem „Freitag, dem 13.“ zu starten, wunderst dich aber, warum er plötzlich jeden einzelnen Freitag im Jahr feuert.
Ein klassischer vergleicht jede volle Minute fünf Felder (Minute, Stunde, Monatstag, Monat, Wochentag) mit der lokalen Systemuhr. Weil Monatstag und Wochentag laut Spezifikation per logischem ODER verknüpft sind und Cloud-Plattformen wie GitHub Actions strikt in UTC rechnen, entstehen ohne Zeitzonen-Prüfung stille Ausfälle. Zudem holt klassisches Cron verpasste Läufe nach einem Server-Neustart nicht nach – hierfür sind systemd-Timer mit Persistent=true die robustere Wahl.
Prüfe jeden Cron-Ausdruck immer gemeinsam mit seiner Ziel-Zeitzone: Cloud-Runner rechnen fast immer in UTC, lokale Linux-Server stolpern zwischen 02:00 und 02:59 Uhr über die Zeitumstellung, und gesetzter Monatstag plus Wochentag wirken als ODER statt UND.
Wie wertet Cron seine fünf Felder aus – und wo lauern die drei teuersten Denkfehler?
Jede Zeile einer Crontab beginnt mit fünf durch Leerzeichen getrennten Zeitfeldern:
- Minute (
0–59): Zu welcher Minute der Stunde soll der Befehl starten? (*/15entspricht0, 15, 30, 45). - Stunde (
0–23): In welcher Stunde des Tages (24-Stunden-Zählung)? - Tag im Monat (
1–31): An welchem Kalendertag? - Monat (
1–12oderJAN–DEC): In welchem Monat? - Wochentag (
0–6bzw.7): An welchem Wochentag (0und7stehen beide für Sonntag)?
In der Praxis sorgen drei Eigenheiten dieses Modells regelmäßig für Produktionsfehler:
1. Die POSIX-ODER-Regel zwischen Monatstag und Wochentag
Solange eines der beiden Tagesfelder auf * steht, verhält sich Cron erwartungsgemäß (15 8 * * 1-5 läuft montags bis freitags um 08:15 Uhr). Sobald jedoch beide Felder vom Sternchen abweichen – etwa 0 9 13 * 5 –, bildet der Standard die Vereinigungsmenge (ODER): Der Job startet am 13. jedes Monats sowie an jedem Freitag. Willst du wirklich eine UND-Bedingung (z. B. „erster Montag im Monat“ für 0 9 1-7 * 1), setzt du in der Crontab nur das Datumsfenster 1-7 und prüfst den Wochentag im Aufrufbefehl.
2. Sommer- und Winterzeit (02:00 bis 02:59 Uhr)
Läuft dein Server in einer Zeitzone mit Sommerzeitumstellung wie , springt die Uhr am letzten März-Sonntag um 02:00 direkt auf 03:00 vor: Ein auf 30 2 * * * geplanter Job fällt an diesem Tag ersatzlos aus. Am letzten Oktober-Sonntag springt die Uhr von 03:00 auf 02:00 zurück – naive Cron-Implementierungen ohne Sonderbehandlung führen Jobs in dieser Stunde doppelt aus.
3. Überlappende Prozesse ohne Sperrdatei (flock)
Cron startet Kommandos blind nach Uhrzeit und prüft nicht, ob der vorherige Lauf desselben Skripts noch arbeitet. Hängt ein */5 * * * *-Import wegen einer langsamen Gegenstelle für 20 Minuten, laufen nach einer Viertelstunde vier identische Prozesse parallel und blockieren Datenbank oder Arbeitsspeicher. Unter Linux schützt das Dienstprogramm flock zuverlässig vor solchen Kaskaden:
# Sichere Produktions-Crontab: explizite Zeitzone + nicht-blockierende Sperrdatei (flock -n)CRON_TZ=Europe/Berlin
# Läuft täglich um 03:15 Uhr (außerhalb des 02:xx-Zeitumstellungsfensters!)15 3 * * * /usr/bin/flock -n /run/lock/nightly-backup.lock /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Echte UND-Verknüpfung für „nur am ersten Montag des Monats um 08:00 Uhr“:# Cron prüft die Tage 1..7, test(1) prüft zusätzlich Wochentag 1 (Montag)0 8 1-7 * * [ "$(date +\%u)" = "1" ] && /usr/local/bin/monthly-report.shBeachte das maskierte Prozentzeichen
\%imdate-Aufruf: In einer Crontab interpretiert der Daemon jedes unmaskierte%als Zeilenumbruch für die Standardeingabe (stdin) – einer der häufigsten Syntaxfehler in Shell-Einzeilern.
Welcher Zeitgeber passt zu welchem Einsatz – Crontab, Systemd-Timer oder GitHub Actions?
- Nächtliche Wartungsfenster in
Europe/Berlinauf 01:xx oder 03:xx / 04:xx Uhr legen, um die 02:00-Uhr-Umstellungsstunde komplett zu meiden - Wiederkehrende Skripte immer idempotent schreiben und mit
flock -nodersystemd-Units gegen parallele Doppelstarts verriegeln - Bei GitHub Actions zusätzlich
workflow_dispatch:aktivieren, damit sich der Job im Ernstfall jederzeit per Klick ohne Wartezeit starten lässt
- Keine minutengenauen Echtzeit-Anforderungen auf GitHub Actions
schedulestützen – der Queue-Verzug auf öffentlichen Runnern schwankt spürbar - Keine ungeschützten
%-Zeichen (wiedate +%F) ohne Backslash\%direkt in eine Crontab-Zeile schreiben - Nicht alle Jobs einer Server-Flotte exakt auf
0 0 * * *(Mitternacht Punkt null) bündeln – das erzeugt künstliche Lastspitzen („Thundering Herd“)
Millionen automatisierter Systeme weltweit feuern exakt zur vollen Stunde (0 * * * *) oder um Mitternacht (0 0 * * *). Wählst du für unkritische Abfragen bewusst eine krumme Minute wie 17 3 * * * oder ergänzt bei systemd RandomizedDelaySec=45s, umgehst du fremde Rate-Limits und überlastete Gegenstellen.
Prüfpunkte vor dem Aktivieren eines neuen Cronjobs
- Zeitzone des Zielsystems verifizieren: Prüfe per
timedatectl(Linux) bzw. in der Plattform-Doku, ob der Scheduler inEurope/BerlinoderUTCrechnet. - März- und Oktober-Sprung gegenprüfen: Stelle im obigen Prüfstand den Zeit-Anker testweise auf März-Sommerzeit und Oktober-Winterzeit, um Ausfälle oder Doppelläufe auszuschließen.
- Umgebungsvariablen (
PATH) explizit setzen: Cron startet mit minimalemPATH=/usr/bin:/bin– verwende absolute Binärpfade wie/usr/local/bin/node, damit Skripte nicht trotz erfolgreichem Handtest im Terminal scheitern. - Fehlerausgabe sichtbar machen: Leite
stderr(2>&1) in ein rotiertes Logfile um oder prüfe den Status beisystemdüberjournalctl -u mein-job.service.
Quellen und nächste Schritte
- The Open Group Base Specifications (IEEE Std 1003.1 / POSIX.1-2024) –
crontabUtility & Day-of-Month / Day-of-Week Specification: pubs.opengroup.org/onlinepubs/9799919799/utilities/crontab.html - freedesktop.org systemd Manual –
systemd.timer&systemd.timeCalendar Events: www.freedesktop.org/software/systemd/man/latest/systemd.timer.html - GitHub Docs – Events that trigger workflows:
schedule(UTC & Queue Latency Notes): docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule - Weiterführend im Digital Garden: Lies in Git Workflows, wie automatisierte Release- und Prüf-Pipelines sauber zusammenspielen, oder prüfe geänderte Server-Konfigurationen vor dem Einspielen lokal im Diff-Checker & Smart-Formatter.