Crontab-Spickzettel: 50+ Cron-Ausdrücke, Syntax und moderner Scheduler-Guide
Ein Cron-Ausdruck besteht aus fünf Feldern (Minute, Stunde, Tag im Monat, Monat, Wochentag), gefolgt von einem Befehl. Diese Grammatik steuert die Unix-Zeitplanung seit 1979 und läuft heute auch unter Kubernetes CronJobs, GitHub Actions, AWS EventBridge und Vercel-Cron-Triggern. Wer sie einmal beherrscht, kann sie überall einsetzen.
Diese Seite richtet sich an Entwicklerinnen und Entwickler, die sofort einen Ausdruck brauchen: eine Linux-Aufgabe, einen Kubernetes CronJob, einen GitHub-Actions-Trigger oder die Fehlersuche, warum ein Fünf-Minuten-Job nur stündlich feuert. Scrollen Sie zur Schnellreferenz-Tabelle für kopierfertige Ausdrücke, springen Sie zu Syntax entschlüsselt für die Feldregeln oder öffnen Sie den Crontab Generator — eine datenschutzfreundliche crontab-guru-Alternative, die im Browser läuft —, um Ausdrücke live zu prüfen.
Schnellreferenz-Tabelle für Cron-Ausdrücke
Dreißig Ausdrücke decken die häufigsten Zeitplanungsfälle ab. Jeder ist gültiges POSIX-Cron mit fünf Feldern: einfügen in crontab -e, in ein Kubernetes-schedule: oder in ein GitHub-Actions-cron:.
| Zeitplan | Cron-Ausdruck | Klartext |
|---|---|---|
| Jede Minute | * * * * * | jede Minute, den ganzen Tag, jeden Tag |
| Alle 5 Minuten | */5 * * * * | Minute 0, 5, 10, …, 55 |
| Alle 15 Minuten | */15 * * * * | Minute 0, 15, 30, 45 |
| Alle 30 Minuten | */30 * * * * | Minute 0 und 30 |
| Jede Stunde | 0 * * * * | zur vollen Stunde |
| Alle 2 Stunden | 0 */2 * * * | Stunde 0, 2, 4, …, 22 |
| Alle 6 Stunden | 0 */6 * * * | Stunde 0, 6, 12, 18 |
| Zweimal täglich (9 Uhr + 21 Uhr) | 0 9,21 * * * | Minute 0 der Stunden 9 und 21 |
| Werktags um 9 Uhr | 0 9 * * 1-5 | Mo–Fr 09:00 |
| Wochenende um 9 Uhr | 0 9 * * 0,6 | Sa und So 09:00 |
| Täglich um Mitternacht | 0 0 * * * | jeden Tag 00:00 |
| Täglich um 2:30 Uhr | 30 2 * * * | ruhiges Batch-Fenster |
| Jeden Montag um 9 Uhr | 0 9 * * 1 | Montags 09:00 |
| Jeden Freitag um 17 Uhr | 0 17 * * 5 | Freitags 17:00 |
| Jeden Sonntag um Mitternacht | 0 0 * * 0 | entspricht @weekly |
| Monatsersten um Mitternacht | 0 0 1 * * | 1. Tag 00:00 (entspricht @monthly) |
| 15. jedes Monats um 12 Uhr | 0 12 15 * * | Gehaltsfenster zur Monatsmitte |
| Letzter-Tag-Prüfung (Wrapper) | 0 0 28-31 * * + Skript | benötigt Datumsprüfung |
| Quartalsweise (1. Jan/Apr/Jul/Okt) | 0 0 1 JAN,APR,JUL,OCT * | erster Tag jedes Quartals |
| Jährlich (1. Januar) | 0 0 1 1 * oder @yearly | Neujahrsmitternacht |
| Alle 5 Min werktags 9–17 Uhr | */5 9-17 * * 1-5 | Polling während Geschäftszeiten |
| Alle 30 Min am Wochenende | */30 * * * 0,6 | Sa/So-Monitoring |
| Zweimal pro Stunde, :15 und :45 | 15,45 * * * * | versetzt zum :00-Ansturm |
| Erster Montag (Wrapper) | 0 9 1-7 * 1 + AND-Prüfung | Wrapper nötig (siehe unten) |
| Makros | @hourly @daily @weekly @monthly @yearly | nicht-standard, aber weit verbreitet |
| Nur beim Neustart | @reboot | nicht-standard, nur Vixie-Cron |
Fügen Sie einen dieser Ausdrücke in den Crontab Generator ein, um die nächsten fünf Auslösungen zu prüfen. Das ist der schnellste Pre-Deploy-Check.
Cron-Syntax entschlüsselt: die 5 Felder
Ein Cron-Ausdruck besteht aus fünf durch Leerzeichen getrennten Feldern und einem Befehl. Jedes Feld steuert ein Segment des Zeitplans. Das ist der Kern der Cron-Ausdrucks-Syntax in jedem Scheduler dieses Guides.
┌──────────── Minute (0 - 59)
│ ┌────────── Stunde (0 - 23)
│ │ ┌──────── Tag im Monat (1 - 31)
│ │ │ ┌────── Monat (1 - 12 oder JAN-DEC)
│ │ │ │ ┌──── Wochentag (0 - 6 oder SUN-SAT; 0 und 7 stehen beide für Sonntag)
│ │ │ │ │
* * * * * auszuführender-Befehl
Eselsbrücke: „Meine Stunde Tut Mir Weh” für Minute, Stunde, Tag im Monat, Monat, Wochentag. Von links nach rechts, von der kleinsten zur größten Einheit.
Erlaubte Werte Feld für Feld
| Feld | Bereich | Aliase | Hinweise |
|---|---|---|---|
| Minute | 0–59 | keine | 0 bedeutet „zur vollen Stunde” |
| Stunde | 0–23 | keine | 24-Stunden-Uhr; 0 ist Mitternacht, 12 ist Mittag |
| Tag im Monat | 1–31 | keine | ungültige Tage feuern stillschweigend nie (31. Februar) |
| Monat | 1–12 | JAN, FEB, MAR, …, DEC | Groß-/Kleinschreibung egal |
| Wochentag | 0–7 | SUN, MON, TUE, …, SAT | 0 und 7 stehen beide für Sonntag |
Operatoren im Detail
Fünf Operatoren decken jeden Standard-Cron-Ausdruck ab:
| Operator | Bedeutung | Beispiel | Bedeutet |
|---|---|---|---|
* | beliebiger Wert | * * * * * | jede Minute |
, | Liste | 0 9,12,17 * * * | 09:00, 12:00, 17:00 |
- | inklusiver Bereich | 0 9-17 * * * | jede Stunde von 09:00 bis 17:00 |
/ | Schritt | */15 * * * * | Minute 0, 15, 30, 45 |
| gemischt | kombiniert | 0 9-12,14-17 * * * | Vormittag + Nachmittag, Mittagspause aussparen |
Beim Schritt-Operator lohnt sich ein zweiter Blick. */N ist am niedrigsten Wert des Feldes verankert, nicht an der aktuellen Uhrzeit. */15 bedeutet „Minute 0, 15, 30, 45 jeder Stunde”, nicht „alle 15 Minuten ab jetzt”. Speichern Sie um 12:03 ab, läuft der nächste Job um 12:15. Mit einer Nicht-Wildcard-Basis liest sich 5/15 als „starte bei 5, dann alle 15”: Minute 5, 20, 35, 50.
Benannte Monate und Wochentage
Schreiben Sie Monate und Wochentage als Namen, Groß-/Kleinschreibung ist egal:
0 0 1 JAN,APR,JUL,OCT * # erster Tag jedes Quartals
0 9 * * MON-FRI # werktags um 9 Uhr
0 17 * * FRI # Freitag um 17 Uhr
Namen sind im Code-Review lesbarer; numerische Formen sind etwas portabler. Wählen Sie pro Projekt einen Stil.
Nicht standardisierte Makros: @reboot, @daily und Verwandte
Die meisten Cron-Implementierungen akzeptieren sechs Abkürzungsmakros:
| Makro | Bedeutet | Bedeutung |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | einmal im Jahr, 1. Januar um Mitternacht |
@monthly | 0 0 1 * * | Monatsersten um Mitternacht |
@weekly | 0 0 * * 0 | jeden Sonntag um Mitternacht |
@daily / @midnight | 0 0 * * * | jeden Tag um Mitternacht |
@hourly | 0 * * * * | zu jeder vollen Stunde |
@reboot | (speziell) | einmal beim Start des Cron-Daemons |
Diese Makros sind nicht standardisiert: Vixie-Cron und Cronie unterstützen sie, Kubernetes CronJob, GitHub Actions und AWS EventBridge nicht. Für portable Ausdrücke schreiben Sie die Fünf-Feld-Form aus. @reboot funktioniert in Containern selten, weil der Cron-Daemon dort meist nicht der Init-Prozess ist.
50+ kopierfertige Cron-Ausdrücke (nach Anwendungsfall gruppiert)
Die Schnellreferenz-Tabelle deckt die häufigen Fälle ab. Dieser Abschnitt geht in sechs Kategorien mit dichteren Cron-Job-Beispielen tiefer.
Alle N Minuten
* * * * * # jede Minute
*/2 * * * * # alle 2 Minuten
*/5 * * * * # alle 5 Minuten — der Klassiker „cron expression every 5 minutes"
*/10 * * * * # alle 10 Minuten
*/15 * * * * # alle 15 Minuten
*/30 * * * * # alle 30 Minuten
0,30 * * * * # explizite Minuten 0 und 30 (gleich wie */30)
*/45 * * * * # WARNUNG: feuert nur bei 0 und 45, dann erfolgt der Wrap
*/45 ist eine klassische Stolperfalle: Minute reicht von 0 bis 59, also landet der Job bei 0 und 45 und springt dann zur nächsten Stunde. Für eine echte 45-Minuten-Kadenz brauchen Sie einen externen Worker.
Stündliche Varianten
0 * * * * # jede Stunde zur :00
30 * * * * # jede Stunde zur :30
0 */2 * * * # alle 2 Stunden, gerade Stunden
0 */6 * * * # alle 6 Stunden
0 */12 * * * # zweimal täglich um 00:00 und 12:00
15 */2 * * * # alle 2 Stunden, um 15 Min versetzt (vermeidet :00-Spike)
Täglich zu festen Zeiten
0 0 * * * # Mitternacht (= @daily / @midnight)
30 2 * * * # 02:30 — ruhiges Batch-Fenster
0 9 * * * # 09:00
45 23 * * * # 23:45 — Tagesabschluss-Rollups
0 9,12,17 * * * # dreimal täglich
0 9-17 * * * # jede Stunde von 09:00 bis 17:00
Wöchentliche Zeitpläne
0 9 * * 1-5 # werktags um 9 Uhr
0 9 * * 0,6 # Wochenende um 9 Uhr
0 18 * * 5 # freitags um 18 Uhr
0 0 * * 0 # Sonntag um Mitternacht (= @weekly)
0 9 * * MON,WED,FRI # Mo/Mi/Fr um 9 Uhr
*/30 9-17 * * 1-5 # alle 30 Min, Geschäftszeiten, werktags
Monatlich und quartalsweise
0 0 1 * * # Monatsersten um Mitternacht (= @monthly)
0 0 15 * * # 15. — Gehaltsfenster
0 0 1,15 * * # 1. und 15. — halbmonatlich
0 0 1 */3 * # quartalsweise: 1. Januar, April, Juli, Oktober
0 0 1 JAN,APR,JUL,OCT * # dasselbe, mit Monatsnamen
0 0 28-31 * * # letzte Tage — mit Datumsprüfungs-Wrapper kombinieren
Für den letzten Tag des Monats gibt es keinen nativen POSIX-Ausdruck. Verwenden Sie einen Wrapper, der date -d tomorrow +%d = 01 prüft, oder einen Scheduler mit nativer Unterstützung (Quartz hat L; Kubernetes nicht).
Jährlich und Makro-Abkürzungen
0 0 1 1 * # 1. Januar um Mitternacht (= @yearly / @annually)
0 0 25 12 * # Weihnachten um Mitternacht
@yearly # = 0 0 1 1 *
@monthly # = 0 0 1 * *
@weekly # = 0 0 * * 0
@daily # = 0 0 * * *
@hourly # = 0 * * * *
@reboot # speziell: einmal beim Daemon-Start (nur Vixie-Cron)
All diese Ausdrücke lassen sich in den Crontab Generator einfügen, um die nächsten fünf Auslösungen zu prüfen. Das ist der günstigste Smoke-Test vor dem Deploy.
Cron vs. systemd-Timer vs. Cloud-Scheduler: Entscheidungsmatrix
Cron ist die Standardwahl, aber nicht immer die beste. Die sieben gängigsten Scheduler im Vergleich. Das hilft bei „cron vs systemd timer”-Entscheidungen, Kubernetes CronJob gegen Vercel-Cron-Job oder der Migration von crontab in die Managed-Cloud.
| Eigenschaft | Vixie-Cron | systemd-Timer | K8s CronJob | GHA schedule | AWS EventBridge | Vercel Cron | Cloudflare Workers |
|---|---|---|---|---|---|---|---|
| Feld-Syntax | 5-Feld POSIX | OnCalendar-Spec | 5-Feld POSIX + timeZone | 5-Feld POSIX | 6-Feld Quartz mit ? | 5-Feld POSIX | 5-Feld POSIX |
| Mindestintervall | 1 Minute | 1 Sekunde | 1 Minute | best effort, ≥15 Min empfohlen | 1 Minute | 1 Minute (Pro-Tarif) | 1 Minute |
| Explizite Zeitzone | CRON_TZ= | Persistent=true | spec.timeZone (1.27+) | nur UTC | ScheduleExpressionTimezone | nur UTC | nur UTC |
| Wiederherstellung verpasster Läufe | nein (anacron) | ja (Persistent=true) | ja (startingDeadlineSeconds) | nein | ja | nein | nein |
| Retry / Backoff | nein | teilweise | ja (backoffLimit) | Retry bei Fehler | ja | nein | ja |
| Concurrency-Kontrolle | nein (flock) | teilweise | ja (concurrencyPolicy) | nein | nein | nein | nein |
@reboot-Unterstützung | ja | ja (via OnBootSec=) | nein | nein | nein | nein | nein |
systemd-Timer: wann Sie Cron vorziehen sollten
Auf systemd-basiertem Linux sind Timer eine ernstzunehmende Alternative: lesbare Kalender-Syntax, Journal-Integration, Wiederherstellung verpasster Läufe. Ein Timer mit zugehörigem Service:
# daily-report.timer
[Unit]
Description=Tagesbericht um 9 Uhr ausführen
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
Unit=daily-report.service
[Install]
WantedBy=timers.target
# daily-report.service
[Unit]
Description=Tagesbericht-Job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-report.sh
User=reporter
Aktivieren mit systemctl enable --now daily-report.timer. Der entscheidende Vorteil ist Persistent=true: war die Maschine um 9 Uhr aus, feuert der Timer sofort beim Booten. Vixie-Cron hat ohne anacron kein Äquivalent. Zur Service-Härtung siehe unsere Security Best Practices.
Kubernetes CronJob
Kubernetes umschließt den POSIX-Zeitplan mit Primitiven für Concurrency, Historie und explizite Zeitzone:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York" # Kubernetes 1.27+
concurrencyPolicy: Forbid # niemals zwei gleichzeitig
startingDeadlineSeconds: 300 # überspringen, wenn >5 Min verzögert
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: reporter
image: reporter:1.4.0
command: ["/usr/local/bin/report.sh"]
concurrencyPolicy: Forbid ist Ihr flock-Äquivalent. Ohne diese Einstellung stapelt sich ein langlaufender Job auf seinen Nachfolger. Sämtliche Stellschrauben finden Sie in der Feld-Referenz.
GitHub Actions: Eigenheiten der Zeitplanung
GitHub Actions akzeptiert standardisierten POSIX-Cron mit fünf Feldern:
on:
schedule:
- cron: '0 9 * * 1-5' # werktags um 9 Uhr UTC
Best Effort: bei hoher Last auf GitHubs Runnern können Jobs Minuten zu spät feuern oder ganz ausfallen. Vermeiden Sie Intervalle unter fünfzehn Minuten. Eine Zeitzonen-Einstellung gibt es nicht, der Zeitplan läuft immer in UTC.
AWS EventBridge: Quartz-Stil mit sechs Feldern
AWS EventBridge verwendet ein Cron im Quartz-Stil mit sechs Feldern und einem verpflichtenden ? in einem der beiden Tagesfelder:
cron(0 9 * * ? *)
Feldreihenfolge: Minuten Stunden Tag-im-Monat Monat Wochentag Jahr. Eines der beiden Tagesfelder muss ? sein, wenn das andere eingeschränkt ist (Quartz’ Antwort auf die POSIX-OR-Mehrdeutigkeit). Wer den Ausdruck direkt aus dem Linux-crontab kopiert, scheitert an der Validierung.
Vercel Cron, Cloudflare Workers, Render Cron Jobs
Neuere Serverless-Plattformen standardisieren auf POSIX mit fünf Feldern. Ein Vercel-Cron-Job lebt in vercel.json als { "crons": [{ "path": "/api/cron/nightly", "schedule": "0 2 * * *" }] }. Cloudflare Workers Cron Triggers nutzen wrangler.toml:
[triggers]
crons = ["*/15 * * * *", "0 9 * * 1-5"]
Render verwendet render.yaml. Alle drei laufen in UTC ohne Zeitzonen-Override pro Zeitplan. Planen Sie von Anfang an in UTC.
7 Cron-Debugging-Fallen (und wie Sie sie aufdecken)
Hinter den meisten Berichten „mein Cron-Job läuft nicht” stecken eine von sieben Ursachen. Arbeiten Sie diese Liste durch, bevor Sie den Scheduler beschuldigen.
Falle 1: PATH ist minimal
Cron startet Jobs mit minimalem $PATH, typischerweise /usr/bin:/bin. Ihre interaktive Shell hat /usr/local/bin, ~/.cargo/bin und ein Dutzend .bashrc-Einträge. Keiner davon existiert in Cron. Das ist die häufigste Ursache von Cron-PATH-Problemen beim Debugging.
Symptom: node: command not found. Lösung: Setzen Sie PATH ganz oben in der crontab oder verwenden Sie absolute Pfade.
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
*/15 * * * * /usr/local/bin/poll-api.sh
0 9 * * * /home/deploy/.cargo/bin/my-rust-cli
Falle 2: stdout und stderr gehen lautlos verloren
Standardmäßig landet Cron-Output in einem Mail-Spool, den niemand liest. Der Job scheitert stillschweigend. Leiten Sie beide Streams um:
*/15 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
Für JSON-Output leiten Sie über jq; zum Extrahieren von Log-Zeilen siehe unseren Regex-Spickzettel. Bei systemd-Timern fängt journalctl -u your-timer.service die Ausgabe ein.
Falle 3: Zeitzonen-Drift zwischen Dev und Prod
Sie schreiben 0 9 * * * auf Ihrem Laptop in New York und erwarten 9 Uhr Eastern. Der Server läuft in UTC. Der Cron feuert um 9 Uhr UTC, also 4 Uhr Eastern, bevor irgendjemand es merkt. Lösung: Server auf UTC stellen und Zeitpläne in UTC schreiben, oder die Zeitzone explizit pinnen.
CRON_TZ=America/New_York
0 9 * * * /usr/local/bin/morning-report.sh
CRON_TZ funktioniert in Vixie-Cron 3.0+; Kubernetes 1.27+ bietet spec.timeZone; AWS EventBridge hat ScheduleExpressionTimezone; GitHub Actions ist immer UTC. Zu UTC, Sommerzeit und Epoch-Arithmetik siehe unseren Unix-Timestamp-Guide.
Falle 4: Unmaskiertes % in Befehlen
Cron behandelt ein unmaskiertes % als Zeilenumbruch: der Rest der Zeile wird dem Befehl als stdin zugeführt. Damit bricht date +"%Y-%m-%d". Maskieren Sie jedes % als \%, oder verlagern Sie die Logik in ein Skript:
0 0 * * * echo "Lauf um $(date +"\%Y-\%m-\%d")" >> /tmp/log
Falle 5: Überlappende Läufe
Ein */5 * * * *-Job, der gelegentlich sieben Minuten dauert, startet die nächste Instanz, bevor die vorherige fertig ist. Zwei Kopien streiten um dieselbe Zeile, dasselbe Lockfile und dasselbe API-Kontingent. Serialisieren Sie mit flock:
*/5 * * * * flock -n /tmp/job.lock /usr/local/bin/job.sh
-n beendet sofort, falls das Lock gehalten wird. Für Kubernetes setzen Sie concurrencyPolicy: Forbid. Bei Lockfile-Berechtigungen lohnt sich ein Blick: siehe Security Best Practices.
Falle 6: @reboot in Containern
@reboot läuft einmal beim Start des Cron-Daemons. In einer VM entspricht das dem Boot. In einem Container ist der Cron-Daemon meist nicht PID 1 und läuft möglicherweise gar nicht. Verwenden Sie @reboot nicht in Containern. Legen Sie Run-once-at-boot-Logik stattdessen in Ihren Entrypoint oder in einen Init-Container.
Falle 7: POSIX-OR-Semantik für Tag im Monat / Wochentag
Die teuerste Cron-Falle. POSIX-Regel: wenn sowohl Tag im Monat als auch Wochentag eingeschränkt sind (keines ist *), feuert der Zeitplan, sobald eines zutrifft.
0 0 1 * 5 sieht aus wie „Mitternacht am 1., nur freitags”, feuert aber am 1. UND an jedem Freitag: sechs bis zehn zusätzliche Auslösungen pro Monat.
# FALSCH: sieht aus wie „1. des Monats, nur wenn Freitag"
0 0 1 * 5
# RICHTIG: nur eine Einschränkung wählen
0 0 1 * * # jeden 1. des Monats
0 0 * * 5 # jeden Freitag
# AND-Semantik braucht einen Wrapper
0 0 1-7 * 5 [ "$(date +\%u)" = "5" ] && /script # nur erster Freitag
Verdächtige Ausdrücke in den Crontab Generator einfügen. Die Next-Run-Vorschau macht die OR-Falle sofort sichtbar.
Moderne Scheduler: wann Cron NICHT geeignet ist
Cron passt für „diesen Befehl ungefähr zu dieser Zeit, in fester Kadenz”. Für mehrere benachbarte Probleme ist er falsch:
- Workflows mit Abhängigkeiten (führe A aus, dann B falls A erfolgreich) → Airflow, Prefect, Dagster.
- Retry, exponentielles Backoff, Dead-Letter-Queues → Temporal, AWS Step Functions, Sidekiq.
- Sub-Minuten-Intervalle → langlebiger Worker, der zwischen Iterationen schläft.
- Sekundengenaue Auslösung → dedizierter Daemon; Managed Scheduler garantieren keine exakten Zeitpunkte.
- Event-getriebene Arbeit → Webhooks, Message-Queues, Change-Data-Capture-Streams.
Cron verschwindet nicht. Airflow, Step Functions und Sidekiq akzeptieren alle Cron-Ausdrücke als Einstieg in ihre Workflows. Die Fünf-Feld-Grammatik ist wiederverwendbar.
Kubernetes-CronJob-Feldreferenz
Die Entscheidungsmatrix oben zeigte einen minimalen CronJob. Die vollständige Feldreferenz für die kubernetes cronjob syntax:
| Feld | Standard | Was es tut |
|---|---|---|
schedule | erforderlich | POSIX-Cron-Ausdruck mit 5 Feldern |
timeZone | Controller-TZ | explizite Zeitzone (1.27+); IANA-Namen verwenden |
concurrencyPolicy | Allow | Forbid überspringt neue Läufe, solange der vorherige aktiv ist; Replace bricht ihn ab |
startingDeadlineSeconds | unbegrenzt | überspringen bei größerer Verzögerung |
successfulJobsHistoryLimit | 3 | erfolgreiche Jobs behalten |
failedJobsHistoryLimit | 1 | fehlgeschlagene Jobs behalten |
suspend | false | pausieren, ohne zu löschen |
backoffLimit | 6 | Pod-Retries, bevor der Job als Failed markiert wird |
activeDeadlineSeconds | nicht gesetzt | harte Obergrenze für die Pod-Laufzeit |
ttlSecondsAfterFinished | nicht gesetzt | Job automatisch nach so vielen Sekunden löschen |
Zwei häufige Stolperfallen: Wer timeZone vergisst, lässt den Zeitplan der Host-Zeitzone des kube-controller-managers folgen (auf Managed Kubernetes unvorhersehbar). Bei einem Minutenplan sammelt der Standardwert successfulJobsHistoryLimit: 3 drei Job-Objekte pro Minute an, solange ttlSecondsAfterFinished nicht gesetzt ist.
Plattformübergreifende Cron-Äquivalente
macOS launchd. Apple empfiehlt launchd anstelle von Cron. Ein launchd-Job ist eine .plist-Datei in ~/Library/LaunchAgents/:
<plist version="1.0"><dict>
<key>Label</key><string>com.example.daily</string>
<key>ProgramArguments</key><array><string>/usr/local/bin/daily.sh</string></array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
</dict></plist>
Laden mit launchctl load ~/Library/LaunchAgents/com.example.daily.plist. Anders als Cron holt launchd verpasste Läufe nach Sleep/Wake nach.
Windows Task Scheduler verwendet schtasks:
schtasks /create /tn "DailyReport" /tr "C:\scripts\report.bat" /sc DAILY /st 09:00
schtasks /create /tn "EveryFifteen" /tr "C:\scripts\poll.bat" /sc MINUTE /mo 15
Unter WSL läuft natives Linux-Cron, hält aber an, sobald die Session endet. Nutzen Sie den Task Scheduler, um dauerhaft laufende WSL-Jobs zu starten.
Cron in Docker-Containern. Die meisten Slim-Images (alpine, debian-slim, distroless) liefern keinen Cron-Daemon mit. Installieren Sie cronie oder busybox-cron und betreiben Sie sie als PID 1 mit tini oder s6-overlay. Fast immer vorzuziehen ist jedoch ein Kubernetes CronJob.
Fortgeschrittene Tipps und Muster
Letzter Tag des Monats
Cron hat keinen nativen „Letzter-Tag”-Operator. Lassen Sie den Job jeden Tag im 28–31-Fenster laufen und prüfen Sie, ob morgen der 1. ist:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh
N-ter Wochentag des Monats
„Erster Montag” nutzt dasselbe Wrapper-Muster: auf Tage 1–7 beschränken, dann den Wochentag prüfen:
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /usr/local/bin/first-monday.sh
Für „letzter Freitag” nehmen Sie die Tage 25–31 plus die Wochentagsprüfung.
Zufälliger Versatz zur Lastverteilung
Wenn viele Maschinen denselben Cron ausführen, erzeugt 0 0 * * * um Mitternacht UTC einen Donnerherden-Effekt. Streuen Sie eine zufällige Verzögerung ein:
RANDOM_DELAY=10 # cronie / anacron, in Minuten
0 0 * * * /usr/local/bin/job.sh
0 0 * * * sleep $((RANDOM \% 600)); /usr/local/bin/job.sh # portabel
Heartbeat-Monitoring
Cron scheitert stillschweigend. Das Dead-Man’s-Switch-Muster funktioniert so: der Job pingt nach jedem erfolgreichen Lauf einen Monitoring-Dienst, und der Dienst alarmiert, wenn ein erwarteter Ping ausbleibt. Healthchecks.io, Cronitor und Dead Man’s Snitch bieten kostenlose Tarife.
*/15 * * * * /usr/local/bin/job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid
Für Monitoring-Logik, die nach Antwort-Codes verzweigt (200 gesund, 429 ratenbegrenzt, 503 degradiert), siehe unseren HTTP-Statuscodes-Spickzettel.
Idempotenz ist eine Eigenschaft des Jobs, nicht des Schedulers
Cron kennt weder Retry noch Wiederherstellung verpasster Läufe noch Concurrency-Kontrolle. Die zuverlässigste Lösung ist, den Job selbst mehrfach-ausführungssicher zu machen. Statt „sende den heutigen Bericht um 9 Uhr” entwerfen Sie ihn als „sende den heutigen Bericht, wenn noch nicht gesendet”. Verpasste Läufe, Duplikate und manuelle Nachholungen münden dann alle in denselben Zustand.
FAQ
Bedeutet */5 * * * * wirklich alle 5 Minuten?
Fast — */5 * * * * ist an Minute 0 verankert, nicht „alle 5 Minuten ab jetzt”. Der Job feuert bei Minute 0, 5, 10, …, 55 jeder Stunde. Der Schritt */N ist relativ zum niedrigsten Wert des Feldes, nicht zur aktuellen Uhrzeit. Speichern Sie um 12:03, läuft der nächste Job um 12:05, nicht um 12:08.
Was bedeutet 0 0 * * * in Cron?
0 0 * * * bedeutet jeden Tag um Mitternacht (00:00) in der lokalen Zeitzone des Servers. Felder: Minute 0, Stunde 0, beliebiger Tag im Monat, beliebiger Monat, beliebiger Wochentag. Entspricht den Makros @daily oder @midnight. Um die Zeitzone zu pinnen, ergänzen Sie CRON_TZ=America/New_York ganz oben in der crontab.
Wie führe ich einen Cron-Job alle 30 Sekunden aus?
Mit Standard-POSIX-Cron geht das nicht: die Mindestgranularität beträgt eine Minute. Drei Workarounds bieten sich an: zwei versetzte Jobs unter * * * * *, einer davon mit sleep 30 &&; ein systemd-Timer mit OnCalendar=*:*:0/30; oder ein langlebiger Worker, der zwischen Iterationen schläft. Die letzte Variante ist meist die richtige.
Welche Zeitzone verwendet Cron standardmäßig?
Die lokale Systemzeitzone des Servers (/etc/timezone oder die Umgebungsvariable TZ). Ein 9-Uhr-Cron auf einem UTC-Server feuert um 4 Uhr US-Ostküste. Lösung: CRON_TZ= oben in der crontab setzen oder Server auf UTC stellen und Zeitpläne in UTC entwerfen. GitHub Actions läuft immer in UTC, Kubernetes 1.27+ unterstützt spec.timeZone.
Warum läuft mein Cron-Job nicht?
Wenn Ihr Cron-Job nicht läuft, prüfen Sie der Reihe nach: Läuft der Cron-Daemon (systemctl status cron)? Ist $PATH in der crontab gesetzt? Wird stderr eingefangen (>> log 2>&1)? Ist die crontab des Nutzers geladen (crontab -l)? Ist % in Befehlen maskiert? Stimmt die Zeitzone mit Ihrer Erwartung überein? Die meisten „läuft nicht”-Meldungen sind der zweite oder dritte Punkt.
Ist die Kubernetes-CronJob-Syntax dieselbe wie Linux-Cron?
Ja für das Schedule-Feld: beide nutzen POSIX-Cron mit fünf Feldern. Kubernetes ergänzt spec.timeZone (1.27+), concurrencyPolicy zur Überlappungskontrolle, startingDeadlineSeconds für die Wiederherstellung verpasster Läufe und suspend: true zum Pausieren. Linux-Cron hat nichts davon, dort greifen Sie zu flock und anacron.
Was ist der Unterschied zwischen @reboot und @daily?
@daily ist ein Makro für 0 0 * * *: jeden Tag um Mitternacht nach festem Zeitplan. @reboot läuft einmal beim Start des Cron-Daemons, ohne wiederkehrenden Zeitplan. @reboot wird von Vixie-Cron und Cronie unterstützt, nicht aber von Kubernetes CronJob, GitHub Actions oder AWS EventBridge. In Containern feuert @reboot selten.
Was ist der Unterschied zwischen cron und crontab?
cron ist der Hintergrund-Daemon, der geplante Jobs ausführt; crontab ist die Datei, die sie auflistet (und der crontab-Befehl zum Bearbeiten dieser Datei). Der Daemon liest die crontab jedes Benutzers in einem Zyklus und führt die Befehle aus, deren Ausführungszeitpunkt dem cron-Ausdruck entspricht. Kurz gesagt: cron ist der Motor, crontab ist das Rezept.