Wie man einen Cron-Ausdruck schreibt: fünf Felder und die ODER-Regel, die niemand erwähnt
Veröffentlicht am 21.5.2026 · 9 Min. Lesezeit · Entwickler-Tools
Daniel Okonkwo — Front-end-Entwickler und Tech-Redakteur bei OneKitly
Web-Performance · Dateiformate
Anhand von 2 Quellen geprüft
Ein Cron-Ausdruck besteht aus fünf durch Leerzeichen getrennten Feldern, von links nach rechts gelesen: Minute (0-59), Stunde (0-23), Tag des Monats (1-31), Monat (1-12 oder dreibuchstabige Namen) und Wochentag (in POSIX 0-6 mit 0 als Sonntag, von den meisten Implementierungen auf 0-7 erweitert, sodass auch 7 Sonntag ist). Ein Sternchen steht für alle Werte im Bereich des Feldes. Ein Bindestrich bildet einen einschließenden Bereich: 8-11 im Stundenfeld sind 8, 9, 10 und 11. Ein Komma bildet eine Liste, und Listen und Bereiche lassen sich mischen: 1-3,7-9. Ein Schrägstrich fügt einen Schritt hinzu, und genau hier geht es beim ersten Mal schief — ein Schritt durchläuft einen Bereich und setzt kein Intervall: */15 im Minutenfeld bedeutet 0, 15, 30 und 45, und */40 bedeutet 0 und 40 und danach nichts mehr, bis die nächste Stunde den Bereich neu startet. Die zweite, größere Falle: Tag des Monats und Wochentag werden mit ODER verknüpft, nicht mit UND. Sind beide eingeschränkt, läuft der Job, sobald eines von beiden zutrifft. 0 0 1 * 1 bedeutet also nicht den ersten Montag des Monats; es läuft um Mitternacht am 1. jedes Monats UND um Mitternacht an jedem Montag. Um einen Job auf einen bestimmten Wochentag festzunageln, lässt man den Tag des Monats als Sternchen stehen — und umgekehrt. Alles Weitere, das dir begegnet — @daily, ein sechstes Sekundenfeld, das Zeichen ?, L, W und # — liegt außerhalb des Standards und hängt vollständig davon ab, welches Cron du betreibst.
Minute, Stunde, Tag des Monats, Monat, Wochentag. Die Fallen: Ein Schritt ist eine Schrittweite durch einen Bereich und kein Intervall, und die beiden Tagesfelder werden mit ODER verknüpft — 0 0 1 * 1 läuft also am 1. und an jedem Montag.
Fünf Felder, und ein Schritt ist kein Intervall
Lies die Felder von links nach rechts, und die Notation passt in den Kopf. Das Sternchen steht für den gesamten Bereich seines Feldes. Der Bindestrich bildet einen einschließenden Bereich: 8-11 im Stundenfeld sind vier Stunden, nicht drei. Das Komma bildet eine Liste, und Listen und Bereiche lassen sich im selben Feld frei kombinieren: 1-3,7-9 ist eine gültige Menge aus sechs Werten. In den Feldern Monat und Wochentag dürfen dreibuchstabige Namen die Zahlen ersetzen, doch klassisches Vixie-Cron akzeptiert weder Bereiche noch Listen aus Namen — 1-3 wird gelesen, wo JAN-MAR scheitern kann, ein guter Grund, in allem Portablen bei Zahlen zu bleiben.
Beim Schrägstrich zerbricht das mentale Modell meist. Ein Schritt durchläuft einen Bereich, er ist kein Wiederholungsintervall, und der Bereich beginnt zu Beginn jeder übergeordneten Einheit von vorn. Im Minutenfeld läuft */15 über 0, 15, 30, 45, dann wechselt die Stunde und es beginnt wieder bei 0 — was zufällig genau alle fünfzehn Minuten ergibt, weshalb niemand den Unterschied bemerkt. Schreibe */40, und die Illusion bricht zusammen: Es läuft über 0 und 40, dann wechselt die Stunde und es beginnt wieder bei 0, was Abstände von vierzig und dann zwanzig Minuten ergibt. Für einen begrenzten Bereich gilt dasselbe: 0-23/2 im Stundenfeld sind die geraden Stunden, und 1-9/2 sind 1, 3, 5, 7 und 9. Brauchst du wirklich ein Intervall, das seine Einheit nicht glatt teilt, kann Cron das nicht in einer Zeile ausdrücken — dann braucht es zwei Einträge oder eine Absicherung im Befehl selbst.
Die beiden Tagesfelder werden mit ODER verknüpft, nicht mit UND
Das ist die Regel, die die meisten Erklärungen falsch wiedergeben, und sie ist keine Eigenart einer Implementierung — sie steht in der Spezifikation. POSIX legt fest: Sind der Tag des Monats als Element oder Liste und ebenso der Wochentag als Element oder Liste angegeben, so gilt jeder Tag, der entweder auf den Tag des Monats oder auf den Wochentag passt. Das Handbuch crontab(5) sagt dasselbe schlichter und liefert das kanonische Beispiel: 30 4 1,15 * 5 läuft um 4:30 Uhr morgens am 1. und am 15. jedes Monats, dazu an jedem Freitag. Nicht am 1. oder 15., wenn diese auf einen Freitag fallen. Beide Mengen, vereinigt.
Die praktischen Regeln daraus sind kurz. Ist genau eines der beiden Tagesfelder ein Sternchen, bestimmt das andere, und es gibt keine Mehrdeutigkeit — dieser Fall sollte fast immer der geschriebene sein. Sind beide Sternchen, passt jeder Tag, ebenfalls eindeutig. Nur wenn beide eingeschränkt sind, greift die Vereinigung, und das Ergebnis ist fast nie das Gemeinte: 0 0 1 * 1 läuft rund fünfmal im Monat statt einmal. Für die Schnittmenge gibt es keine Cron-Syntax; brauchst du wirklich den ersten Montag des Monats, ist der übliche Kniff, die Vereinigung zu planen und sie im Befehl einzuengen — auf 0 0 1-7 * 1 laufen lassen und das Skript mit einer Prüfung öffnen, dass der Wochentag Montag ist, oder auf 0 0 * * 1 mit einer Prüfung, dass der Tag des Monats höchstens 7 ist. Crons im Quartz-Stil bieten dafür einen Operator #, im System-Cron gibt es ihn nicht.
Alles jenseits der fünf Felder ist implementierungsabhängig
POSIX definiert fünf Felder, das Sternchen, Bereiche, Listen — sonst nichts. Jede Bequemlichkeit darüber hinaus ist eine Erweiterung, und Erweiterungen unterscheiden sich. Der Schrägstrich-Schritt ist eine Vixie-Erweiterung, heute nahezu universell, aber weiterhin nicht im Standard. Die Kurzformen — @yearly mit dem Synonym @annually, @monthly, @weekly, @daily mit dem Synonym @midnight, @hourly und @reboot — stammen aus derselben Linie, und gerade @reboot hat systemübergreifend keine feste Bedeutung, weil vom Dienst abhängt, was als Neustart gilt. Ein sechstes Feld für Sekunden ist in Anwendungs-Schedulern wie Quartz, Spring und mehreren Node-Bibliotheken üblich und fehlt im System-Cron: Ein aus einer Framework-Dokumentation in eine Crontab kopierter Ausdruck ist um ein Feld verschoben und plant etwas völlig anderes, statt laut zu scheitern.
Das Fragezeichen gehört zur selben Familie. In Quartz bedeutet es kein bestimmter Wert und existiert genau dafür, die Mehrdeutigkeit zwischen Tag des Monats und Wochentag aufzulösen, indem ein Feld für unerheblich erklärt wird; System-Cron akzeptiert es überhaupt nicht. Die Operatoren L, W und # für letzter Tag des Monats, nächster Werktag und n-ter Wochentag sind ebenfalls nur in Quartz zu Hause. Zwei Umgebungsfakten wiegen schwerer als diese ganze Syntax: Der Dienst führt Jobs in der Zeitzone aus, für die er konfiguriert ist, sodass ein in einer Umgebung korrekter Ausdruck in einer anderen eine Stunde zu früh oder zu spät auslöst, und rund um eine Zeitumstellung läuft ein in der übersprungenen Stunde geplanter Job womöglich gar nicht, während einer in der wiederholten Stunde zweimal laufen kann. Und Cron ist ein Auslöser, kein Job-Runner — keine Wiederholungen, keine Nebenläufigkeitskontrolle, keine Erinnerung daran, dass der letzte Lauf fehlschlug. Dürfen sich zwei Aufrufe nicht überlappen, setze die Sperre im Befehl selbst.
| Position | Feld | Zulässige Werte | Der Fehler, zu dem es verleitet |
|---|---|---|---|
| 1. | Minute | 0-59 | Hier * schreiben, wo 0 gemeint war — ein Ausdruck wie * 3 * * * läuft sechzigmal, in jeder Minute der 3-Uhr-Stunde |
| 2. | Stunde | 0-23 | In einer 12-Stunden-Uhr denken: Es gibt keine 24 und kein pm, Mitternacht ist 0 und 23 Uhr ist 23 |
| 3. | Tag des Monats | 1-31 | 31 verwenden und zwölf Läufe im Jahr erwarten: Kürzere Monate treffen schlicht nie zu, der Job überspringt also stillschweigend Februar, April, Juni, September und November |
| 4. | Monat | 1-12 oder JAN bis DEC | Annehmen, dass Namen überall funktionieren: Klassisches Vixie-Cron akzeptiert dreibuchstabige Namen, lehnt aber Bereiche oder Listen davon ab — JAN-MAR kann also scheitern, wo 1-3 funktioniert |
| 5. | Wochentag | In POSIX 0-6 mit 0 als Sonntag; in den meisten Implementierungen 0-7, wobei auch 7 Sonntag ist | Dieses Feld und den Tag des Monats gleichzeitig einschränken: Beide werden mit ODER verknüpft, der Job läuft also an beiden Tagesmengen statt an deren Schnittmenge |
Häufige Fragen
- Was bedeutet */15 genau?
- Im Minutenfeld wählt es die Werte 0, 15, 30 und 45 — eine Schrittweite von fünfzehn durch den Bereich 0 bis 59, beginnend beim ersten Wert des Bereichs. Es ist kein Timer, der fünfzehn Minuten nach dem letzten Lauf auslöst, und es überlebt keine unsaubere Teilung: */40 im Minutenfeld wählt nur 0 und 40, die Abstände betragen also vierzig Minuten und dann zwanzig, wenn die nächste Stunde den Bereich neu startet. Willst du ein Intervall, das ab dem letzten Lauf und nicht ab dem Stundenbeginn misst, ist Cron das falsche Werkzeug — ein systemd-Timer mit OnUnitActiveSec oder eine Schleife in einem langlebigen Prozess ist das richtige.
- Wie führe ich einen Job alle 90 Minuten aus?
- Nicht in einer Zeile, denn 90 teilt 60 nicht, und ein Schritt bleibt auf ein einzelnes Feld beschränkt. Das Muster wiederholt sich allerdings alle drei Stunden, sodass zwei Einträge es exakt abdecken: 0 */3 * * * ergibt 00:00, 03:00, 06:00 und so fort, und 30 1-23/3 * * * ergibt 01:30, 04:30, 07:30 und so fort. Zusammen ergeben sie einen Lauf alle neunzig Minuten, und die Folge schließt um Mitternacht sauber, weil vierundzwanzig Stunden genau sechzehn Neunzig-Minuten-Intervalle sind. Derselbe Zwei-Einträge-Kniff funktioniert für jedes Intervall, das eine ganze Stundenzahl glatt teilt; was das nicht tut, etwa alle 50 Minuten, lässt sich in Cron überhaupt nicht ausdrücken.
- Warum lief mein nächtlicher Job zweimal — oder gar nicht?
- Die übliche Ursache ist eine Zeitumstellung. Cron vergleicht die Uhrzeit an der Wand: Springt die Uhr vor, wird eine nie existierende Stunde auch nie getroffen, und ein darin geplanter Job läuft schlicht nicht; fällt die Uhr zurück, tritt eine Stunde zweimal auf, und ein darin geplanter Job kann zweimal auslösen. Die Implementierungen unterscheiden sich darin, wie sehr sie das ausgleichen — deshalb verhält sich dieselbe Crontab auf zwei Distributionen anders. Verlässliche Abhilfen: heikle Jobs außerhalb des Umstellungsfensters planen, den Dienst in einer Zeitzone ohne Umstellungen betreiben, oder den Befehl selbst gefahrlos zweimal ausführbar machen. Letzteres lohnt sich ohnehin: Idempotenz schützt auch vor Wiederholungen, überlappenden Läufen und manuellem Nachstarten.
Artikel, die dich interessieren könnten
Alle Ratgeber →Ähnliche Tools
Quellen
Hast du einen Fehler in diesem Artikel entdeckt?