# Cron Job Helper — Tipps & Tricks

> Stolperfallen bei Cron: Tag-des-Monats und Wochentag als OR-Logik, Server-Zeitzone, Output-Unterdrückung, MAILTO, absolute Pfade und Lock gegen Überlappung.

Source: https://www.jpkc.com/db/tools/cron/tips/

Zurück zur Übersicht: [Cron Job Helper](https://www.jpkc.com/db/tools/cron/) · Tool live öffnen: [www.jpkc.com/tools/cron/](https://www.jpkc.com/tools/cron/)

Das [Manual](https://www.jpkc.com/db/tools/cron/manual/) erklärt jeden Reiter, die [Beispiele](https://www.jpkc.com/db/tools/cron/examples/) zeigen konkrete Ausdrücke. Hier geht es um das, was beim Cron-Schreiben gern schiefgeht — die Fallen, die man erst beim dritten verpassten Lauf bemerkt.

## 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 auf `*` steht), 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 * 5
```

Das läuft **nicht** „nur an einem Freitag, den 13.", sondern an **jedem 13. des Monats UND zusätzlich an jedem Freitag**. Willst du „Freitag, der 13.", geht das mit Standard-Cron nicht in einer Zeile.

Praktische Konsequenz: Schränke immer **nur eines** der beiden Tag-Felder ein und lass das andere auf `*`. Die Klartext-Vorschau des Builders formuliert solche Fälle als „on day-of-month X and weekday Y" — verlass dich bei der Bedeutung trotzdem auf die OR-Regel des echten Cron, denn so verhält sich der Daemon auf dem Server.

## 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". Zwei Dinge folgen daraus:

- Auf dem System kannst du die Zeitzone der Crontab mit `CRON_TZ=Europe/Berlin` am **Anfang** der Datei festlegen (so empfiehlt es auch der *General Best Practices*-Block des Tools). In Containern setzt du stattdessen `TZ=Europe/Berlin` als Umgebungsvariable — sonst läuft der Container in UTC.
- Die **Next Scheduled Runs** im Builder rechnen in der **Zeitzone deines Browsers**. Diese Vorschau ist eine bequeme Schätzung, aber nicht zwingend identisch mit dem, was der Server tut. Plane in Serverzeit, wenn es auf die Stunde ankommt.

## 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>&1` hinter 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`) gibt es dieses Feld **nicht** (der Job läuft als der jeweilige Benutzer), in den system-weiten Dateien ist es Pflicht. Die CMS-Rezepte des Tools zeigen die system-weite Form mit `www-data`.

## Ü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>&1
```

`flock -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: immer `-s` (silent) bei curl, 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.

## Quartz-Zeichen funktionieren nicht im Unix-Cron

Die *Reference*-Tabelle listet `?`, `L`, `W` und `#` und markiert sie ausdrücklich als **Quartz**. Diese Zeichen gehören zum Java-Quartz-Scheduler (und einigen Cron-Derivaten), **nicht** zum normalen Unix-`cron`. Ein `0 0 L * *` („letzter Tag des Monats") oder `5#3` („3. Freitag") wird von vixie-/cronie-Cron nicht verstanden. Nutze sie nur, wenn dein Ziel-Scheduler Quartz-Syntax spricht; für eine klassische `crontab` bleib bei `* , - /`.

## Container: 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](https://www.jpkc.com/db/tools/docker/)** — wenn du die Container-Muster aus dem *Docker / DDEV*-Tab in ein echtes Setup gießt.
- **[Zeit- & Timestamp-Tools](https://www.jpkc.com/db/tools/time/)** — um die Server-Zeitzone und Unix-Timestamps zu klären, bevor du dich auf eine Uhrzeit festlegst.

---

Mehr Kontext: die [Übersicht](https://www.jpkc.com/db/tools/cron/) zum großen Bild, das [Manual](https://www.jpkc.com/db/tools/cron/manual/) für jeden Reiter und die [Beispiele](https://www.jpkc.com/db/tools/cron/examples/) für konkrete Ausdrücke. Ausprobieren kannst du alles direkt im [Tool](https://www.jpkc.com/tools/cron/).

