Cron Job Helper — Tipps & Tricks
Cron-Fallen: ODER-Logik der Tagesfelder, Zeitzone und Sommerzeit, % in der Crontab, Schritte über die Stunde, MAILTO, absolute Pfade, flock.
Zurück zur Übersicht: Cron Job Helper · Tool live öffnen: www.jpkc.com/tools/cron/
Das Manual erklärt jeden Reiter, die Beispiele zeigen konkrete Ausdrücke. Hier geht es um das, was beim Cron-Schreiben gern schiefgeht — die Fallen, die du erst beim dritten verpassten Lauf bemerkst.
Die berüchtigte OR-Logik bei Tag-des-Monats und Wochentag
Die wichtigste Cron-Falle überhaupt: Wenn du sowohl das Feld Day of Month als auch das Feld Day of Week einschränkst (also keines mit * beginnt), verknüpft der klassische Unix-Cron sie mit ODER, nicht mit UND. Der Job läuft dann an jedem Tag, der eine der beiden Bedingungen erfüllt.
Beispiel:
0 0 13 * 5Das läuft nicht „nur an einem Freitag, den 13.“, sondern an jedem 13. des Monats UND zusätzlich an jedem Freitag. Der Cron Job Helper zeigt dir das unmissverständlich: Der Klartext lautet „At 00:00, on day 13 of the month OR on Friday (either one matches)“, und die Next Scheduled Runs listen entsprechend jeden Freitag und jeden 13.
Praktische Konsequenz: Schränke immer nur eines der beiden Tag-Felder ein und lass das andere auf *. Willst du wirklich „Freitag, der 13.“, schränkst du per Cron nur den Tag ein und prüfst den Wochentag im Befehl — nach demselben Muster, das der Reference-Tab für „letzter Tag des Monats“ zeigt:
0 0 13 * * [ "$(date +\%u)" = 5 ] && /usr/local/bin/job.shdate +%u liefert den Wochentag als 1 (Montag) bis 7 (Sonntag); das % muss in der Crontab als \% stehen. Und Vorsicht mit Schritten: Ein Feld, das mit * beginnt, gilt auch als */2 noch als uneingeschränkt — 0 0 */2 * 1 läuft deshalb nur an Montagen mit ungeradem Tagesdatum, nicht „jeden zweiten Tag oder montags“.
Cron läuft in der Server-Zeitzone — nicht deiner
Cron nutzt die Zeitzone des Servers (oft UTC), nicht deine lokale. Ein 0 3 * * * ist „3 Uhr Serverzeit“. Drei Dinge folgen daraus:
- Prüfe die Zeitzone auf dem Server mit
date +%Zodertimedatectl. cronie liest zusätzlichCRON_TZ=Europe/Berlinam Anfang der Crontab (so empfiehlt es auch der General Best Practices-Block des Tools). In Containern setzt duTZ=Europe/Berlinals Umgebungsvariable — sonst läuft der Container in UTC. - Die Next Scheduled Runs im Builder rechnen standardmäßig in der Zeitzone deines Browsers. Stell unter Time zone of the server die Zeitzone deines Servers ein, dann zeigt die Vorschau die Läufe so, wie der Server sie ausführt — samt Zonenkürzel und relativer Zeit. Die Auswahl bleibt in deinem Browser gespeichert.
- Für die Zeitumstellung gilt eine eigene Regel — siehe den nächsten Abschnitt.
Sommerzeit: Jobs zwischen 2 und 3 Uhr
In Zeitzonen mit Sommerzeit gibt es eine Nacht ohne 02:00–02:59 und eine Nacht, in der diese Stunde doppelt vorkommt. cronie behandelt das je nach Job unterschiedlich, und das Tool rechnet genau so:
- Feste Uhrzeit (Minute und Stunde ohne
*, etwa30 2 * * *): Fällt der Termin in die übersprungene Stunde, läuft der Job direkt nach dem Sprung; in der doppelten Stunde läuft er nur einmal. - Wildcard-Jobs (etwa
*/15 2 * * *oder* * * * *): Sie folgen der Wanduhr — in der übersprungenen Stunde läuft nichts, die doppelte Stunde läuft zweimal.
Betroffene Läufe markiert die Vorschau mit einem gelben Hinweis („DST: …“). Wer solche Sonderfälle vermeiden will, plant kritische Jobs außerhalb von 02:00–03:00 oder betreibt den Server in UTC. Ein durchgerechnetes Beispiel steht unter Beispiele.
Output unterdrücken: MAILTO vs. /dev/null
Standardmäßig schickt Cron jede Ausgabe (stdout und stderr) eines Jobs per Mail an den lokalen Systembenutzer. Ein gesprächiges Skript flutet so das Postfach. Es gibt zwei Hebel, die der Reference-Tab klar trennt:
MAILTO=""am Anfang der Crontab schaltet die Mails global für die ganze Datei ab.> /dev/null 2>&1hinter dem Befehl unterdrückt die Ausgabe pro Job — und verhindert zusätzlich, dass ein Prozess an einer vollen Mail-Queue hängenbleibt.
Für Web-Cron-Jobs ist die Kombination beider am robustesten. Wichtig dabei: > /dev/null 2>&1 leitet auch stderr (2>) um — > /dev/null allein würde Fehlermeldungen weiterhin mailen.
Absolute Pfade verwenden
Cron startet mit einer minimalen Umgebung: $PATH ist meist nur /usr/bin:/bin, dein interaktives Profil (.bashrc, nvm, rbenv …) wird nicht geladen. Ein Befehl, der in deiner Shell läuft, kann im Cron stumm scheitern, weil das Binary nicht gefunden wird. Deshalb: absolute Pfade zu Interpreter und Skript (/usr/bin/php /var/www/html/artisan …) statt bloß php …. Wenn ein Skript bestimmte Umgebungsvariablen braucht, setze sie explizit am Anfang der Crontab.
Den Benutzer angeben (bei system-weiten Crontabs)
In /etc/crontab und Dateien unter /etc/cron.d/ steht vor dem Befehl der Benutzername — z. B. www-data. Das ist ein häufiger Stolperstein: In der persönlichen Crontab (crontab -e, für einen anderen Benutzer crontab -u www-data -e) gibt es dieses Feld nicht (der Job läuft als der jeweilige Benutzer), in den system-weiten Dateien ist es Pflicht. Steht in einer persönlichen Crontab trotzdem www-data vor dem Befehl, versucht Cron, ein Programm namens www-data zu starten. Die CMS-Rezepte des Tools sind für crontab -u www-data -e geschrieben und kommen deshalb ohne Benutzerspalte aus; für eine Datei unter /etc/cron.d/ ergänzt du den Benutzer zwischen Zeitplan und Befehl.
Überlappende Läufe verhindern (flock)
Wenn ein Job länger braucht als sein Intervall (z. B. ein */5-Import, der manchmal 7 Minuten dauert), startet Cron den nächsten Lauf trotzdem — zwei Instanzen rennen parallel und können sich gegenseitig in die Quere kommen. Sperre dagegen mit flock:
*/5 * * * * flock -n /tmp/mycron.lock php /var/www/html/artisan schedule:run >> /dev/null 2>&1flock -n startet den Job nur, wenn die Lock-Datei frei ist, und überspringt ihn sonst. Das Tool nennt diesen Kniff im General Best Practices-Block.
Web-Cron vs. System-Cron — wann was
Der Web Cron-Tab erzeugt Jobs, die per curl/wget eine URL aufrufen — der Zeitplan läuft also weiterhin im System-Cron, nur der Befehl ist ein HTTP-Request. Wann lohnt sich das?
- Web-Cron ist die richtige Wahl auf Shared Hosting, wo du keinen Shell-Zugriff oder keine Kontrolle über CLI-Tools hast, und für CMS-Endpunkte, die ohnehin per HTTP angestoßen werden (
wp-cron.php, Drupals/cron/KEY). - System-Cron mit CLI (WP-CLI, Drush,
artisan,console) ist schneller, robuster und umgeht den Webserver-Timeout. Wo du Shell-Zugriff hast, ist er die bessere Wahl — der CMS & Frameworks-Tab empfiehlt durchweg den CLI-Weg.
Schützt du einen Web-Cron-Endpunkt, denk an die Web Cron Tips aus dem Tool: die Antwort mit -o /dev/null verwerfen und -sS setzen (still, aber echte Fehler bleiben sichtbar), Ausgabe in eine Logdatei, die URL mit Token/IP-Restriktion/Basic-Auth absichern, einen Timeout setzen und ausschließlich HTTPS verwenden, damit Zugangsdaten nicht im Klartext gehen. Der Generator setzt außerdem immer curl --fail: Antwortet der Endpunkt mit 4xx oder 5xx, endet curl mit einem Fehlercode, statt den Fehler stillschweigend als Erfolg zu verbuchen.
Für Basic-Auth gilt: Ein Passwort in der Befehlszeile steht in der Crontab und ist während des Laufs für andere Benutzer in der Prozessliste (ps) sichtbar. Aktivierst du im Tool Read credentials from ~/.netrc und trägst einen HTTP Auth User ein, steht in der Zeile nur --netrc, und das Tool liefert dir die passende .netrc-Zeile, die du mit chmod 600 schützt. Ohne Benutzer greift der Haken nicht: curl bekommt dann gar keine Auth-Option, und wegen --fail scheitert jeder Lauf an HTTP 401. Das Passwort kannst du leer lassen und erst in der .netrc eintragen.
Das Prozentzeichen beendet den Befehl
Eine Eigenheit der Crontab, die selbst erfahrene Admins erwischt: Ein unmaskiertes % beendet den Befehl, alles danach wird ihm als Eingabe übergeben. Ein date +%Y-%m-%d im Dateinamen eines Backups oder eine URL mit %20 zerbricht so lautlos. In der Crontab schreibst du deshalb jedes % als \%.
Der Web Cron-Generator erledigt das automatisch: Die Generated Crontab Line enthält \%, die Ausgabe Command only dagegen die unmaskierte Fassung, die du direkt in der Shell testen kannst. Kopierst du einen Befehl aus der Shell von Hand in die Crontab, musst du selbst daran denken.
Schritte über die Stundengrenze: */7 ist nicht alle 7 Minuten
Ein Schritt beginnt in jeder Stunde (bzw. an jedem Tag) neu bei 0. */7 im Minutenfeld läuft zur Minute 0, 7, … 56 — und dann wieder zur Minute 0. Zwischen 56 und 0 liegen nur vier Minuten. Ebenso läuft 0 */5 * * * um 0, 5, 10, 15 und 20 Uhr und danach schon wieder um Mitternacht. Der Builder warnt bei solchen Schritten und nennt die tatsächliche Lücke. Für gleichmäßige Abstände nimmst du einen Schritt, der 60 (Minute) bzw. 24 (Stunde) glatt teilt.
Ähnlich tückisch ist die Kurzform 5/15: cronie liest sie als 5-59/15, andere Cron-Varianten lehnen sie ab. Das Tool akzeptiert sie mit Warnung — schreib zur Sicherheit die ausgeschriebene Form.
Quartz-Zeichen funktionieren nicht im Unix-Cron
Die Zeichen ?, L, W und # sowie ein Sekundenfeld gehören zu Quartz, Spring und anderen Schedulern, nicht zum normalen Unix-cron. Ein 0 0 L * * („letzter Tag des Monats“) oder 5#3 („3. Freitag“) wird von Vixie cron und cronie nicht verstanden. Der Cron Job Helper weist solche Ausdrücke deshalb als Fehler ab, und ein sechstes Zeitfeld quittiert er mit einer Warnung, weil Cron es als Befehl lesen würde. Für eine klassische crontab bleib bei * , - /.
Den letzten Tag des Monats bekommst du trotzdem — mit der Umschreibung aus dem Reference-Tab: Der Job startet an den Tagen 28–31 und prüft selbst, ob morgen der Erste ist.
0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && commandContainer: nach stdout loggen
In Docker/Podman solltest du Cron-Ausgabe nach /proc/1/fd/1 (bzw. stdout) schreiben, damit sie in docker logs auftaucht — sonst verschwindet sie im Nichts. Und denk an die Zeitzone: ohne TZ läuft der Container in UTC. Beides steht in den Container Cron Tips des Tools.
Mit anderen JPKCom-Tools kombinieren
- Docker-Tools — wenn du die Container-Muster aus dem Docker / DDEV-Tab in ein echtes Setup gießt.
- Zeit- & Timestamp-Tools — um die Server-Zeitzone und Unix-Timestamps zu klären, bevor du dich auf eine Uhrzeit festlegst.
Mehr Kontext: die Übersicht zum großen Bild, das Manual für jeden Reiter und die Beispiele für konkrete Ausdrücke. Ausprobieren kannst du alles direkt im Tool.