Crontab-Syntax meistern: Zeitausdrücke, Sonderzeichen und Fallstricke
AI generated
$
/etc
Linux · Cron · Crontab · Scheduling
Crontab-Syntax meistern
fünf Felder, unendlich viele Stolperfallen

Die fünf Felder einer Crontab-Zeile wirken auf den ersten Blick simpel, doch Listen, Bereiche, Schrittweiten und vor allem die berüchtigte OR-Logik zwischen Tag und Wochentag führen regelmäßig zu Jobs, die zur falschen Zeit oder gar nicht laufen. Dieser Leitfaden erklärt die Crontab-Syntax vollständig, inklusive Sonderzeichen, Umgebungsvariablen und Zeitzonen.

13 Min. Lesezeit crontab · @reboot · CRON_TZ · cron.d Linux · Debian/Ubuntu · RHEL

1. Die fünf Felder der Crontab

Jede Zeile einer Crontab besteht aus fünf Zeitfeldern, gefolgt vom eigentlichen Befehl: Minute, Stunde, Tag des Monats, Monat und Wochentag, in genau dieser Reihenfolge. Das erste Feld akzeptiert Werte von null bis 59, das zweite null bis 23, das dritte eins bis 31, das vierte eins bis zwölf und das fünfte null bis sieben, wobei sowohl null als auch sieben den Sonntag repräsentieren. Ein Stern in einem Feld bedeutet, dass jeder mögliche Wert dieses Feldes zutrifft, wodurch * * * * * einen Job jede einzelne Minute ausführt.

Die korrekte Crontab-Syntax zu verstehen bedeutet vor allem, sich die Reihenfolge dieser fünf Felder einzuprägen, denn ein vertauschtes Minuten- und Stundenfeld führt nicht zu einem Fehler, sondern zu einem Job, der zur falschen Zeit läuft, oft unbemerkt über Wochen. Ein Job, der um 14:30 Uhr laufen soll, braucht die Zeile 30 14 * * *, nicht 14 30 * * *, was Minute 14 und Stunde 30 bedeuten würde und wegen des ungültigen Stundenwerts entweder ignoriert wird oder je nach Cron-Implementierung einen Parserfehler erzeugt.

Ein weiteres grundlegendes Detail der Crontab-Syntax: Jeder Benutzer hat seine eigene persönliche Crontab, bearbeitet mit crontab -e und gespeichert unter /var/spool/cron/crontabs/benutzername. Diese persönliche Crontab läuft grundsätzlich mit den Rechten des jeweiligen Benutzers, was bei Systemaufgaben, die Root-Rechte benötigen, eine bewusste Entscheidung zwischen sudo crontab -e für root und dedizierten Systemverzeichnissen wie /etc/cron.d erfordert.

2. Listen, Bereiche und Schrittweiten kombinieren

Neben einzelnen Werten und dem Stern kennt die Crontab-Syntax drei weitere Operatoren, die sich beliebig kombinieren lassen. Ein Komma erzeugt eine Liste einzelner Werte, etwa 0,15,30,45 für vier feste Minuten innerhalb jeder Stunde. Ein Bindestrich definiert einen zusammenhängenden Bereich, etwa 9-17 für die Stunden zwischen 9 und 17 Uhr. Ein Schrägstrich definiert eine Schrittweite, wobei */15 im Minutenfeld exakt alle 15 Minuten bedeutet, beginnend bei Minute null.

Diese Operatoren lassen sich verschachteln, was die Ausdruckskraft der Crontab-Syntax erheblich erweitert: 9-17/2 im Stundenfeld bedeutet jede zweite Stunde innerhalb des Bereichs von 9 bis 17 Uhr, also 9, 11, 13, 15 und 17 Uhr. Wichtig zu verstehen: Eine Schrittweite ohne vorangestellten Bereich, also */15, beginnt immer bei dem kleinstmöglichen Wert des Feldes, nicht beim aktuellen Zeitpunkt der Bearbeitung. Ein häufiger Irrtum ist die Annahme, */15 würde ab der Uhrzeit der Eintragung zählen, tatsächlich zählt es immer ab Minute null.


# Combining lists, ranges, and steps in crontab time fields
# Format: minute hour day-of-month month day-of-week command

# Every 15 minutes, on the hour, quarter, half, and three-quarter mark
*/15 * * * * /usr/local/bin/health-check.sh

# At minute 0 and 30 of every hour between 9 and 17 (business hours)
0,30 9-17 * * * /usr/local/bin/sync-inventory.sh

# Every second hour within a range, e.g. 9, 11, 13, 15, 17
0 9-17/2 * * * /usr/local/bin/generate-report.sh

# Combined list and range: run at minute 5 on Mon, Wed, and Fri
5 0 * * 1,3,5 /usr/local/bin/weekly-partial-backup.sh

# Every 5th day of the month, at 03:00
0 3 */5 * * /usr/local/bin/monthly-cleanup.sh

3. Die Sonderzeichen @reboot, @daily und Co.

Für die häufigsten Zeitpläne bietet die Crontab-Syntax eine lesbarere Kurzform in Form von speziellen Zeichenfolgen, die anstelle der fünf Zeitfelder stehen. @reboot führt einen Befehl exakt einmal nach jedem Systemstart aus, unabhängig von einer konkreten Uhrzeit, was sich hervorragend für das Wiederherstellen eines definierten Systemzustands eignet, etwa das erneute Einrichten von iptables-Regeln oder das Starten eines Hintergrunddienstes, der nicht über systemd verwaltet wird.

@daily entspricht 0 0 * * * und läuft täglich um Mitternacht, @weekly entspricht 0 0 * * 0 und läuft sonntags um Mitternacht, @monthly entspricht 0 0 1 * * und läuft am ersten Tag jedes Monats, @hourly entspricht 0 * * * * und läuft zu jeder vollen Stunde. Diese Kurzformen verbessern die Lesbarkeit erheblich, verstecken aber den genauen Ausführungszeitpunkt hinter einem Namen, weshalb sie sich vor allem für unkritische, klar benannte Wartungsjobs eignen, weniger für Aufgaben mit exaktem, betrieblich relevantem Zeitfenster.


# Special strings replace the five time fields entirely
@reboot /usr/local/bin/restore-iptables-rules.sh
@daily  /usr/local/bin/rotate-application-logs.sh
@weekly /usr/local/bin/full-backup.sh
@monthly /usr/local/bin/generate-monthly-invoice-report.sh
@hourly  /usr/local/bin/check-disk-usage.sh

# Equivalent explicit five-field expressions, for comparison
# @daily   ==  0 0 * * *
# @weekly  ==  0 0 * * 0
# @monthly ==  0 0 1 * *
# @hourly  ==  0 * * * *

4. Tag und Wochentag gleichzeitig: die OR-Logik-Falle

Die mit Abstand häufigste Verwechslung in der Crontab-Syntax betrifft das gleichzeitige Setzen von Tag des Monats und Wochentag auf konkrete, vom Stern abweichende Werte. Intuitiv würde man erwarten, dass beide Bedingungen mit UND verknüpft werden, also nur dann ausgeführt wird, wenn beide Bedingungen gleichzeitig zutreffen. Tatsächlich verknüpft Cron diese beiden Felder mit ODER, sobald keines der beiden ein Stern ist: Der Job läuft, wenn entweder der Tag des Monats ODER der Wochentag zutrifft, nicht nur wenn beide gleichzeitig zutreffen.

Ein konkretes Beispiel macht die Falle sichtbar: 0 0 15 * 1 soll vermeintlich am 15. eines Monats ausgeführt werden, aber nur, wenn dieser Tag ein Montag ist. Tatsächlich läuft dieser Job aber an jedem 15. des Monats UND an jedem Montag, unabhängig vom Kalendertag. Wer eine echte UND-Verknüpfung zwischen Tag und Wochentag braucht, muss beide Felder auf Stern belassen und die Bedingung stattdessen im aufgerufenen Skript selbst prüfen, etwa mit [[ "$(date +%d)" == "15" && "$(date +%u)" == "1" ]] als Guard-Klausel am Skriptanfang.


# The OR trap: day-of-month and day-of-week combine with OR,
# not AND, whenever BOTH fields are restricted (not "*")

# WRONG assumption: "runs on the 15th, only if it's a Monday"
# ACTUAL behavior: runs on every 15th AND every Monday
0 0 15 * 1 /usr/local/bin/misleading-job.sh

# CORRECT approach for true AND logic: leave both fields as "*"
# and check the condition explicitly inside the script
0 0 * * * /usr/local/bin/guarded-job.sh

# guarded-job.sh contents:
#!/usr/bin/env bash
set -euo pipefail
if [[ "$(date +%d)" == "15" && "$(date +%u)" == "1" ]]; then
  /usr/local/bin/actual-task.sh
fi

5. Umgebungsvariablen in Crontab-Dateien

Cron startet Jobs standardmäßig mit einer stark minimierten Umgebung, die sich fundamental von einer interaktiven Shell-Sitzung unterscheidet. Das PATH-Wert enthält oft nur /usr/bin:/bin, wodurch selbst installierte Werkzeuge in /usr/local/bin oder benutzerspezifische Verzeichnisse nicht gefunden werden, sofern der vollständige Pfad nicht explizit im Skript oder im PATH der Crontab selbst gesetzt wird. Diese minimale Umgebung ist die häufigste Ursache dafür, dass ein Skript, das im Terminal einwandfrei funktioniert, als Cron-Job mit command not found scheitert.

Die Crontab-Syntax erlaubt eigene Variablendefinitionen als separate Zeilen oberhalb der eigentlichen Zeitfelder, etwa PATH=/usr/local/bin:/usr/bin:/bin, SHELL=/bin/bash oder MAILTO=admin@example.com. Diese Variablen gelten für alle nachfolgenden Zeilen derselben Crontab-Datei, lassen sich aber mehrfach überschreiben, falls unterschiedliche Jobs unterschiedliche Umgebungen brauchen. Wichtig: Diese Variablen wirken nur innerhalb der Crontab selbst, sie werden nicht automatisch an aufgerufene Skripte weitergereicht, wenn diese ihrerseits eine eigene, restriktivere Shell-Konfiguration laden.


# crontab -e content: variable definitions above the time fields
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin
MAILTO=admin@example.com

# Jobs below inherit the PATH, SHELL, and MAILTO defined above
0 3 * * * /usr/local/bin/nightly-backup.sh
*/10 * * * * /usr/local/bin/check-queue-depth.sh

# Override MAILTO for one noisy job only
MAILTO=""
* * * * * /usr/local/bin/high-frequency-heartbeat.sh

# Restore default MAILTO for jobs below this line
MAILTO=admin@example.com
0 0 * * 0 /usr/local/bin/weekly-summary.sh

6. crontab -e vs. /etc/cron.d vs. /etc/cron.daily

Neben der persönlichen Crontab über crontab -e kennt Linux mehrere weitere Orte für zeitgesteuerte Aufgaben, jeder mit eigenen Eigenschaften. Das Verzeichnis /etc/cron.d enthält eigenständige Dateien im selben Fünf-Felder-Format, jedoch mit einer zusätzlichen Spalte für den ausführenden Benutzernamen direkt nach den Zeitfeldern, was root-only-Verwaltung durch Paketmanager und Konfigurationsmanagement-Tools wie Ansible erheblich vereinfacht, ohne die persönliche Crontab eines Benutzers zu berühren.

Die Verzeichnisse /etc/cron.daily, /etc/cron.weekly und /etc/cron.monthly folgen einem anderen Prinzip: Statt einer Crontab-Datei mit Zeitfeldern liegen dort ausführbare Skripte, die von einem übergeordneten, meist über /etc/crontab gesteuerten Mechanismus zum jeweils passenden Intervall aufgerufen werden. Der genaue Ausführungszeitpunkt dieser Verzeichnisse ist über /etc/crontab oder, bei modernen Systemen, über den systemd-Timer anacron beziehungsweise cron.daily.timer konfiguriert und nicht in den einzelnen Skripten selbst sichtbar.

7. Zeitzonen in Crontabs: CRON_TZ und Systemzeitzone

Standardmäßig interpretiert Cron alle Zeitfelder in der Systemzeitzone, wie sie mit timedatectl konfiguriert ist. Für Server, die für mehrere Regionen arbeiten oder deren Systemzeitzone bewusst auf UTC gesetzt ist, aber einzelne Jobs an einer lokalen Geschäftszeit ausgerichtet sein sollen, unterstützen moderne Cron-Implementierungen wie Vixie-Cron die Variable CRON_TZ, gesetzt als eigene Zeile innerhalb der Crontab, ähnlich wie PATH oder MAILTO.

CRON_TZ=Europe/Berlin oberhalb einer Gruppe von Zeilen sorgt dafür, dass diese Jobs unabhängig von der Systemzeitzone stets nach mitteleuropäischer Zeit ausgeführt werden, selbst wenn der Server selbst UTC als Systemzeitzone führt. Das ist besonders relevant für Aufgaben, die an eine reale, menschliche Geschäftszeit gebunden sind, etwa einen Reminder um 9 Uhr morgens Ortszeit, während rein technische Wartungsjobs meist unabhängig von der Zeitzone in UTC bleiben können, um Sommerzeitumstellungen komplett zu vermeiden.


# Server system timezone is UTC, but these specific jobs
# should always fire at 09:00 local Berlin time, DST-aware
CRON_TZ=Europe/Berlin
0 9 * * 1-5 /usr/local/bin/send-morning-standup-reminder.sh

# Reset to system default (UTC) for the remaining, timezone-agnostic jobs
CRON_TZ=UTC
0 2 * * * /usr/local/bin/nightly-maintenance.sh

8. Häufige Syntax-Fallstricke und wie man sie testet

Neben der bereits behandelten OR-Logik-Falle gibt es weitere wiederkehrende Fehlerquellen in der Crontab-Syntax. Ein fehlendes Newline-Zeichen am Ende der Crontab-Datei führt bei manchen Cron-Implementierungen dazu, dass die letzte Zeile komplett ignoriert wird, ein besonders tückischer Fehler, weil er sich in einem Text-Editor kaum bemerkbar macht. Ein zu enges Zeitfenster wie 59 23 31 12 *, gedacht für Silvester, feuert nur, wenn der 31. Dezember tatsächlich existiert und auf einen gültigen Wochentag fällt, was bei fälschlich zusätzlich gesetztem Wochentagsfeld erneut in die OR-Falle führt.

Für das Testen komplexer Zeitausdrücke, bevor sie produktiv gesetzt werden, ist ein externer Cron-Parser wie crontab.guru unverzichtbar: Die Eingabe eines Fünf-Felder-Ausdrucks liefert sofort eine menschenlesbare Beschreibung sowie eine Liste der nächsten Ausführungszeitpunkte, wodurch Missverständnisse über Bereiche, Schrittweiten oder die OR-Logik bereits vor dem produktiven Einsatz auffallen. Zusätzlich lohnt sich nach jeder Änderung ein Blick in journalctl -u cron oder grep CRON /var/log/syslog, um zu bestätigen, dass ein Job tatsächlich zum erwarteten Zeitpunkt ausgelöst wurde.

9. Crontab-Syntax-Elemente im Überblick

Die folgende Tabelle fasst die wichtigsten Elemente der Crontab-Syntax und ihre jeweilige Bedeutung kompakt zusammen.

Element Beispiel Bedeutung Stolperfalle
Stern * Jeder mögliche Wert des Feldes Keine, Grundbaustein
Liste 0,15,30,45 Genau diese Werte Keine Leerzeichen nach Komma erlaubt
Bereich 9-17 Zusammenhängender Wertebereich Grenzen selbst mit eingeschlossen
Schrittweite */15 Alle 15 Einheiten ab dem Feld-Minimum Zählt ab null, nicht ab Eintragungszeit
Tag + Wochentag 15 * 1 ODER-Verknüpfung, nicht UND Häufigste Fehlannahme überhaupt

Wer diese Elemente der Crontab-Syntax einmal verinnerlicht hat, insbesondere die OR-Logik zwischen Tag des Monats und Wochentag, vermeidet die häufigsten Fehler, die sonst erst Wochen später auffallen, wenn ein Job zu einem unerwarteten Zeitpunkt oder gar nicht läuft.

Mironsoft

Cron-Audits, Deployment-Automatisierung und Linux-Systemadministration

Cron-Jobs, die zur exakt richtigen Zeit laufen?

Wir prüfen bestehende Crontab-Einträge auf typische Syntax-Fallstricke, insbesondere die OR-Logik zwischen Tag und Wochentag, und dokumentieren Zeitzonen-Abhängigkeiten sauber mit CRON_TZ.

Crontab-Audit

Systematische Prüfung aller Crontab-Zeilen auf Syntax-Fallstricke

Migration nach cron.d

Persönliche Crontabs sauber in versionierte /etc/cron.d-Dateien überführen

Zeitzonen-Konzept

CRON_TZ konsequent für lokal gebundene Jobs einsetzen

10. Zusammenfassung

Die Crontab-Syntax besteht aus fünf Zeitfeldern, Minute, Stunde, Tag des Monats, Monat und Wochentag, kombinierbar mit Listen, Bereichen und Schrittweiten. Sonderzeichen wie @reboot und @daily verbessern die Lesbarkeit für häufige Zeitpläne. Die größte Stolperfalle bleibt die ODER-Verknüpfung zwischen Tag des Monats und Wochentag, sobald beide Felder gleichzeitig eingeschränkt sind, was eine explizite Prüfung im Skript selbst erfordert, wenn eine echte UND-Logik gewünscht ist.

Umgebungsvariablen wie PATH, SHELL und MAILTO gehören an den Anfang der Crontab-Datei, CRON_TZ steuert die Zeitzone einzelner Job-Gruppen unabhängig von der Systemzeitzone. Externe Werkzeuge wie crontab.guru helfen, komplexe Zeitausdrücke vor dem produktiven Einsatz zu verifizieren und die häufigsten Syntax-Fallstricke frühzeitig zu erkennen.

Crontab-Syntax, das Wichtigste auf einen Blick

Fünf Felder

Minute, Stunde, Tag des Monats, Monat, Wochentag, in genau dieser Reihenfolge.

Listen, Bereiche, Schritte

Komma für Listen, Bindestrich für Bereiche, Schrägstrich für Schrittweiten, beliebig kombinierbar.

OR-Logik-Falle

Tag des Monats und Wochentag werden mit ODER verknüpft, sobald beide eingeschränkt sind.

CRON_TZ

Steuert die Zeitzone einzelner Job-Gruppen unabhängig von der Systemzeitzone.

11. FAQ: Crontab-Syntax

1Reihenfolge der fünf Crontab-Felder?
Minute, Stunde, Tag des Monats, Monat, Wochentag.
2Warum läuft mein Job zu oft?
Tag und Wochentag werden mit ODER verknüpft, sobald beide konkret gesetzt sind.
3Ab wann zählt */15?
Immer ab dem Feld-Minimum, also Minute null.
4Was bedeutet @reboot?
Einmalige Ausführung nach jedem Systemstart.
5Warum findet Cron Kommandos nicht?
Zu kurzer Standard-PATH, explizit in der Crontab ergänzen.
6crontab -e vs. /etc/cron.d?
cron.d hat eine zusätzliche Benutzerspalte, ideal für Paketverwaltung.
7Wie setze ich eine eigene Zeitzone?
Mit CRON_TZ als eigener Zeile oberhalb der betroffenen Jobs.
8Wie teste ich komplexe Ausdrücke?
Mit crontab.guru vor dem produktiven Einsatz verifizieren.
9Warum wird die letzte Zeile ignoriert?
Fehlendes Newline-Zeichen am Dateiende.
10Wie erzwinge ich echte UND-Logik?
Beide Felder auf Stern setzen und im Skript selbst prüfen.