Der komplette Leitfaden zu Cron-Ausdrücken: Fünf Felder, klassische Fallen und Debugging-Tipps
2026-08-06
Fast jeder Backend-Entwickler hat diesen Moment erlebt: vor einer Zeile aus fünf Sternchen in einer Crontab starren und sich still fragen „ist das jede Minute oder jede Stunde?“ Crons Syntax ist nur eine Handvoll Symbole, doch sie trägt vier Jahrzehnte historischen Ballast und echt kontraintuitive Designs: Sonntag hat zwei legale Werte, Tag des Monats und Wochentag kombinieren mit ODER statt UND, und für 2:30 Uhr geplante Jobs verschwinden einmal im Jahr in Luft. Diese Anleitung geht die Regeln und die Szenarien durch, die in der Produktion am ehesten brechen.
Die fünf Felder: Anatomie eines Cron-Ausdrucks
Ein standardmäßiger Cron-Ausdruck hat fünf Felder, durch Leerzeichen getrennt:
┌───────────── Minute (0-59)
│ ┌─────────── Stunde (0-23)
│ │ ┌───────── Tag des Monats (1-31)
│ │ │ ┌─────── Monat (1-12)
│ │ │ │ ┌───── Wochentag (0-7, 0 und 7 sind beide Sonntag)
│ │ │ │ │
* * * * *
Jedes Feld kann einen konkreten Wert, einen Bereich, eine Liste oder * für „jeder Wert“ halten. Der Job feuert nur, wenn die aktuelle Zeit auf alle Felder gleichzeitig passt (mit einer berühmten Ausnahme, unten behandelt). Beachten Sie, dass Crons feinste Granularität die Minute ist — es gibt kein Sekundenfeld, „alle 30 Sekunden“ ist also unmöglich; Sie bräuchten eine Schleife im Skript oder einen anderen Scheduler.
Das Wochentagfeld verdient Betonung: sowohl 0 als auch 7 bedeuten Sonntag, mit 1 bis 6 für Montag bis Samstag. Das ist historischer Rückstand — verschiedene Unix-Varianten wählten verschiedene Konventionen, und irgendwann wurden beide akzeptiert. Bleiben Sie in neuen Ausdrücken bei 0, um Mehrdeutigkeit zu vermeiden. Viele Implementierungen akzeptieren auch dreibuchstabige Namen wie SUN und MON, die lesbarer sind, aber prüfen Sie die Unterstützung, bevor Sie sich in einem portablen Skript darauf verlassen.
Die vier Operatoren: *, /, ,, -
Alle Variation innerhalb eines Felds kommt von vier Symbolen:
*— Wildcard, matcht jeden legalen Wert.*im Minutenfeld bedeutet jede Minute.*/n— Schritt, „alle n“.*/5im Minutenfeld bedeutet Minuten 0, 5, 10, 15 und so weiter.,— Liste, zählt Werte auf.1,15im Tag-des-Monats-Feld bedeutet den 1. und 15.-— Bereich.1-5im Wochentagfeld bedeutet Montag bis Freitag.
Kombiniert erzeugen sie die Alltagsmuster:
*/5 * * * * alle 5 Minuten
0 */2 * * * zur Minute 0 jeder 2. Stunde
0 9-18 * * 1-5 zur vollen Stunde, 9–18 Uhr, werktags
0 0 1,15 * * um Mitternacht am 1. und 15.
Hier versteckt sich eine klassische Fehllesung: */5 und 5 sind völlig verschieden. */5 ist „alle 5 Minuten“ — zwölf Läufe pro Stunde —, während 5 * * * * „zur Minute 5 jeder Stunde“ ist — vierundzwanzig Läufe pro Tag. Die Vorfallbericht-Vorlage „ich wollte alle 5 Minuten, schrieb aber 5 * * * *“ wurde von Generationen von Ingenieuren ausgefüllt. Schritte kombinieren sich auch mit Bereichen: 10-30/10 bedeutet Minuten 10, 20 und 30.
Sind Sie unsicher bei einem Ausdruck, simulieren Sie ihn nicht im Kopf — bauen Sie ihn visuell im Cron-Generator, der jedes Feld erklärt und die kommenden Feuereiten listet. Das ist schneller als Fingergymnastik.
Gängige Zeitpläne, Feld für Feld
Hier sind die Zeitpläne, die Sie in der Produktion am häufigsten sehen, aufgeschlüsselt:
0 3 * * *
│ │ │ │ └── Wochentag: beliebig
│ │ │ └──── Monat: beliebig
│ │ └────── Tag des Monats: beliebig
│ └──────── Stunde: 3
└────────── Minute: 0
→ 3:00 Uhr täglich — Primetime für Log-Rotation und Backups
30 2 * * 0
→ 2:30 Uhr jeden Sonntag — ein guter Slot für vollständige Datenneuaufbauten
*/10 8-19 * * 1-5
→ alle 10 Minuten von 8:00 bis 19:59 an Werktagen — Health Checks zu Geschäftszeiten
0 0 1 * *
→ Mitternacht am 1. jedes Monats — monatliche Berichtserzeugung
Über die Fünf-Feld-Syntax hinaus akzeptieren viele Cron-Implementierungen @-Kurzformen: @daily entspricht 0 0 * * *, @hourly entspricht 0 * * * *, @weekly ist 0 0 * * 0, @monthly ist 0 0 1 * *, und @yearly (oder @annually) ist 0 0 1 1 *. Es gibt auch das spezielle @reboot, das einmal beim Systemstart läuft. Diese sind maximal lesbar — nutzen Sie sie, wenn sie passen.
Die größte Falle: Tag des Monats ODER Wochentag
Nun die Ausnahme. Normalerweise kombinieren die fünf Felder mit UND-Logik, aber wenn sowohl Tag des Monats als auch Wochentag eingeschränkt sind (keines ist *), feuert Cron, wenn eines von beiden passt — ODER, nicht UND.
Diese Regel beißt Sie irgendwann, meist zu einer ungünstigen Stunde. Sagen wir, Sie wollen „läuft am Freitag, dem 13.“ (der klassische Aberglaube-Scherz) und schreiben:
0 0 13 * 5
Sie erwarten, dass es nur an einem Freitag, dem 13., feuert. In Wahrheit feuert es am 13. jedes Monats und zusätzlich an jedem Freitag. Die Cron-Manpage sagt das klar, aber die meisten lernen es zuerst in einem Postmortem. Der korrekte Ansatz: Lassen Sie Cron eine Bedingung handhaben und Ihr Skript die andere. Schreiben Sie 0 0 13 * * und prüfen Sie oben im Skript, ob heute Freitag ist, und beenden Sie früh, wenn nicht.
Zeitzonen und Sommerzeit: Jobs, die verschwinden, und Jobs, die doppelt laufen
Cron nutzt standardmäßig die lokale Zeitzone des Systems. Das klingt harmlos, bis Ihr Server in ein neues Rechenzentrum umzieht, Ihr Container-Image standardmäßig UTC nutzt oder die Sommerzeit ankommt.
Die Umstellungstage der Sommerzeit sind, wo die Magie passiert. Wenn die Uhren vorrücken, existieren die lokalen Zeiten von 2:00 bis 2:59 Uhr nicht — ein für 30 2 * * * geplanter Job überspringt diesen Tag einfach. Wenn die Uhren zurückfallen, passiert die Stunde von 1:00 bis 1:59 Uhr zweimal, und Jobs in diesem Fenster laufen zweimal. Schickt Ihr Job Rechnungen oder Push-Benachrichtigungen, bedeutet doppelte Ausführung echtes Geld und verärgerte Nutzer. Drei Abhilfen: Verlegen Sie kritische Jobs in ein sicheres Fenster (nach 3:30 Uhr vermeidet in praktisch jeder Zeitzone die Sommerzeit-Übergänge); betreiben Sie den Server in UTC und schreiben Sie Ausdrücke in UTC, den Zeitstempel-Konverter nutzend, wenn Sie zurück in lokale Zeit abbilden müssen; und machen Sie den Job idempotent, damit ein doppelter Lauf harmlos ist.
Noch ein Hinweis für verteilte Teams: Nennen Sie immer die Zeitzone, wenn Sie einen Zeitplan besprechen. „9 Uhr“ bedeutet über Kontinente hinweg nichts. Um zu prüfen, was ein gegebener UTC-Zeitplan lokal bedeutet, zeigt der Zeitstempel-Konverter Umrechnungen nebeneinander, und der Datumsrechner ermittelt das konkrete Kalenderdatum eines Zeitplans N Tage von jetzt.
Crontab vs. systemd-Timer vs. CI-Scheduler
Die Fünf-Feld-Syntax ist eine Lingua franca, aber jeder Scheduler spricht seinen eigenen Dialekt, und Migrationen sind, wo Dinge schiefgehen.
Traditionelle Crontab: bearbeitet mit crontab -e, Granularität auf Minutenebene, Feldreihenfolge exakt wie oben. Vixie cron und seine Nachfahren (der Standard auf den meisten Linux-Distributionen) folgen jeder Regel in diesem Artikel, einschließlich der ODER-Semantik.
systemd-Timer: der empfohlene Ansatz auf modernen Distributionen. Sie nutzen gar keine Fünf-Feld-Syntax — stattdessen haben sie ihr eigenes OnCalendar-Format, wie OnCalendar=Mon..Fri 09:00. Die Vorteile sind real: vollständiges Logging über journald, Sekundengenauigkeit, Persistent=true, um während der Auszeit der Maschine verpasste Läufe nachzuholen, und ausdrückliche Abhängigkeitsreihenfolge. Der Preis ist eine weitere zu lernende Syntax und subtil andere Feldsemantik als klassisches Cron.
GitHub Actions: Der schedule-Trigger in einem Workflow nutzt Fünf-Feld-Cron, das identisch zur Crontab aussieht, mit einem tödlichen Unterschied — es wird immer in UTC interpretiert. Schreiben Sie 0 9 * * * in der Erwartung eines 9-Uhr-Laufs, und Sie bekommen 17 Uhr, wenn Ihr Team in Peking sitzt (UTC+8). GitHubs eigene Dokumentation warnt außerdem, dass geplante Workflows unter Last deutlich verzögert sein können — behandeln Sie einen CI-Zeitplan nie als präzisen Wecker. GitLab-CI-Pipeline-Schedules lassen Sie dagegen pro Zeitplan eine Zeitzone wählen, eine dritte Semantik. Bevor Sie eine Crontab in einen CI-Scheduler migrieren, validieren Sie die Feuereiten des Ausdrucks im Cron-Generator mit Blick auf UTC neu.
Debugging: Der Job lief nicht — was nun?
Stille Cron-Fehler sind ein Initiationsritual. Arbeiten Sie diese Checkliste durch:
- Prüfen Sie zuerst die Logs:
grep CRON /var/log/syslogunter Debian/Ubuntu oder/var/log/cronunter RHEL/CentOS. Bestätigen Sie, dass der Daemon den Job tatsächlich ausgelöst hat. - Führen Sie das Skript manuell aus: Crons Umgebung ist minimal —
PATHist typischerweise nur/usr/bin:/bin, in Ihrer interaktiven Shell verfügbare Befehle existieren für Cron also womöglich nicht. Nutzen Sie absolute Pfade in Skripten oder setzen SiePATHausdrücklich oben in der Crontab. - Die Prozent-Falle:
%ist ein Sonderzeichen in Crontab-Einträgen (es bedeutet Zeilenumbruch),date +\%Y-\%m-\%dbraucht also Escaping. In einer Skriptdatei nicht. - Validieren Sie den Ausdruck selbst: Fügen Sie ihn in den Cron-Generator ein und prüfen Sie, ob die gelisteten künftigen Feuereiten das sind, was Sie beabsichtigten — achten Sie besonders auf
*/5, das als5geschrieben wurde, und falsche Wochentagsnummern. - Prüfen Sie die Zeitzone: Ist der Server auf UTC oder lokaler Zeit?
timedatectlantwortet in einem Befehl; raten Sie nicht. - Fangen Sie die Ausgabe ab: Cron mailt stdout/stderr an den lokalen Nutzer, was niemand liest. Leiten Sie ausdrücklich um:
*/5 * * * * /opt/job.sh >> /var/log/job.log 2>&1.
Cron ist ein reiner Ausdruck der Unix-Philosophie: ein einfaches Werkzeug, das eine Sache tut und nie aus der Mode kommt. Der Preis dieser Langlebigkeit ist eine Handvoll kontraintuitiver Designs — Feldsemantik, ODER-Logik, Zeitzonenverhalten —, die auf die Uneingeweihten lauern. Verinnerlichen Sie die Regeln in dieser Anleitung, und Ihre geplanten Jobs werden deutlich leiser und Ihre Postmortems deutlich seltener.