Zum Inhalt springen
Kaspar

Cronjobs überwachen mit Heartbeats: So merkst du sofort, wenn ein Job ausfällt

Warum Cronjobs still scheitern, wie Heartbeat-Monitoring funktioniert und wie du Backups, Importe und Queues mit Ping-URLs, Toleranzzeit und Laufzeitgrenze zuverlässig überwachst.

Kaspar Team 9 Min. Lesezeit

Ein Cronjob, der nicht läuft, macht keinen Lärm. Es gibt keine Fehlerseite, keinen roten Balken im Browser und meistens auch keine E-Mail. Das nächtliche Backup fehlt einfach, der Produktimport bleibt auf dem Stand von letzter Woche, und die Rechnungen werden nicht verschickt. Auffallen tut das oft erst, wenn jemand die Daten braucht.

In diesem Artikel zeigen wir dir, warum klassische Überwachung bei Cronjobs an ihre Grenzen stößt, wie Heartbeat-Monitoring das Problem umdreht und wie du deine wichtigsten Jobs in wenigen Minuten absicherst.

Warum Cronjobs still scheitern

Cron ist eines der zuverlässigsten Werkzeuge im Linux-Umfeld. Der Daemon startet Befehle zu festen Zeiten, mehr nicht. Genau diese Einfachheit ist auch die Schwäche: Cron selbst weiß nicht, ob ein Job erfolgreich war. Typische Ursachen für stille Ausfälle sind:

  • Der Job startet gar nicht. Ein Tippfehler in der Crontab, ein Server, der nach einem Neustart ohne den Cron-Dienst hochkommt, ein Container, in dem Cron nie lief, oder ein Deployment, das die Crontab überschrieben hat.
  • Die Umgebung ist anders als in deiner Shell. Cron startet mit einem minimalen PATH und ohne deine Profil-Dateien. Ein Befehl, der im Terminal funktioniert, findet unter Cron plötzlich php oder node nicht.
  • Der Job bricht mit einem Fehler ab. Eine Datenbank ist nicht erreichbar, ein API-Schlüssel ist abgelaufen, die Festplatte ist voll.
  • Der Job hängt. Ein Netzwerk-Request ohne Timeout oder ein Lock, das nie freigegeben wird, und der Prozess läuft stundenlang, ohne etwas zu tun.
  • Die Fehlermeldung geht ins Leere. Cron verschickt Ausgaben per Mail an den lokalen Benutzer, sofern MAILTO gesetzt und ein Mailserver eingerichtet ist. Auf vielen Servern landet diese Mail nirgendwo, oder in einem Postfach, das niemand liest.

All diesen Fällen ist gemeinsam: Von außen sieht alles normal aus. Deine Website ist online, der Server antwortet auf Pings. Ein Uptime-Check, der eine URL abfragt, bemerkt davon nichts.

Das Prinzip: Heartbeat statt Abfrage

Bei klassischem Uptime-Monitoring fragt ein externer Dienst regelmäßig nach: „Bist du noch da?" Bei Cronjobs funktioniert das nicht, weil es nichts gibt, das man abfragen könnte. Ein Job existiert nur für die Dauer seiner Ausführung.

Heartbeat-Monitoring dreht die Richtung um. Nicht der Monitoring-Dienst fragt den Job, sondern der Job meldet sich beim Monitoring-Dienst. Jeder Job bekommt eine eigene, geheime Ping-URL. Am Ende eines erfolgreichen Laufs ruft der Job diese URL auf. Der Dienst kennt den Zeitplan und weiß deshalb, wann der nächste Ping fällig ist. Bleibt er aus, ist das ein Alarm.

Das Schöne daran: Es spielt keine Rolle, warum der Ping fehlt. Ob die Crontab gelöscht wurde, der Server aus ist oder das Skript bei Zeile 40 abstürzt, das Ergebnis ist immer dasselbe: kein Signal, also eine Benachrichtigung. Du musst nicht jeden möglichen Fehler vorhersehen.

Die drei Signale: Start, Erfolg und Fehler

Ein einfacher Heartbeat am Ende des Jobs deckt bereits die meisten Fälle ab. Noch aussagekräftiger wird die Überwachung mit drei Signalen:

  1. Start: Der Job meldet, dass er begonnen hat. So lässt sich messen, wie lange ein Lauf dauert, und ein hängender Job fällt auf, weil auf den Start kein Ende folgt.
  2. Erfolg: Der Job ist sauber durchgelaufen.
  3. Fehler: Der Job weiß selbst, dass etwas schiefgegangen ist, und meldet das aktiv. Dann musst du nicht erst auf das Ende der Toleranzzeit warten.

In Kaspar sieht das so aus. Jeder Cron-Monitor hat eine Ping-URL der Form https://kaspar.app/p/<token>. Daran hängst du optional ein Suffix:

Aufruf Bedeutung
/p/<token> Lauf erfolgreich beendet
/p/<token>/start Lauf hat begonnen
/p/<token>/fail Lauf ist fehlgeschlagen
/p/<token>/<exitcode> Lauf beendet, Ergebnis anhand des Exit-Codes (0 = Erfolg, alles andere = Fehler)

Die Endpunkte akzeptieren GET, POST und HEAD. Bei POST kannst du zusätzlich einen Ausschnitt der Job-Ausgabe im Request-Body mitschicken, damit du im Fehlerfall direkt siehst, was passiert ist.

Zeitplan, Toleranz und maximale Laufzeit

Damit der Monitoring-Dienst weiß, wann ein Ping fällig ist, hinterlegst du den Zeitplan. Das geht entweder als festes Intervall („alle 15 Minuten") oder als Cron-Ausdruck, also genau so, wie er in deiner Crontab steht, etwa 30 2 * * * für täglich um 2:30 Uhr.

Zwei weitere Einstellungen entscheiden darüber, ob du sinnvolle Alarme oder lästige Fehlalarme bekommst:

Toleranzzeit (Grace Period). Kein Job endet auf die Sekunde genau. Ein Backup dauert an manchen Tagen fünf Minuten, an anderen zwanzig. Die Toleranzzeit legt fest, wie lange nach dem erwarteten Zeitpunkt gewartet wird, bevor ein fehlender Ping als Ausfall gilt. Als Faustregel: etwas mehr als die längste normale Laufzeit, die du beobachtet hast.

Maximale Laufzeit. Wenn dein Job ein Start-Signal sendet, kann der Dienst prüfen, ob auf den Start rechtzeitig ein Ende folgt. Läuft ein Job deutlich länger als üblich, ist das oft ein frühes Warnsignal: wachsende Datenmengen, ein langsamer externer Dienst oder ein Prozess, der hängt.

Praxis: Heartbeats in deine Crontab einbauen

Das einfachste Muster hängt den Ping mit && an den eigentlichen Befehl. Der Ping wird nur gesendet, wenn der Befehl mit Exit-Code 0 endet:

30 2 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 --retry 3 https://kaspar.app/p/DEIN_TOKEN > /dev/null

Die curl-Optionen sind bewusst gewählt:

  • -f lässt curl bei HTTP-Fehlern mit einem Fehlercode enden, statt die Fehlerseite still auszugeben.
  • -sS unterdrückt die Fortschrittsanzeige, zeigt aber echte Fehler weiterhin an.
  • -m 10 begrenzt den Request auf zehn Sekunden. So kann der Ping selbst deinen Job nie blockieren.
  • --retry 3 wiederholt den Request bei vorübergehenden Netzwerkproblemen.

Exit-Code melden statt nur Erfolg

Mit && erfährst du nur, dass etwas schiefging, wenn die Toleranzzeit abgelaufen ist. Schneller geht es, wenn du den Exit-Code immer meldest:

30 2 * * * /usr/local/bin/backup.sh; curl -fsS -m 10 --retry 3 https://kaspar.app/p/DEIN_TOKEN/$? > /dev/null

Das Semikolon sorgt dafür, dass curl in jedem Fall läuft, und $? enthält den Exit-Code des vorherigen Befehls. Endet das Backup mit Code 0, gilt der Lauf als erfolgreich. Bei jedem anderen Code bekommst du sofort eine Benachrichtigung. Achtung: In einer Crontab musst du ein % als \% schreiben, $? ist dagegen unproblematisch.

Start und Ende in einem Wrapper-Skript

Für Jobs, deren Laufzeit du im Blick behalten willst, lohnt sich ein kleines Wrapper-Skript:

#!/usr/bin/env bash
set -uo pipefail

PING="https://kaspar.app/p/DEIN_TOKEN"

curl -fsS -m 10 --retry 3 "$PING/start" > /dev/null

output=$(/usr/local/bin/backup.sh 2>&1)
status=$?

curl -fsS -m 10 --retry 3 --data-raw "$(printf '%s' "$output" | tail -c 10000)" "$PING/$status" > /dev/null

exit $status

Das Skript meldet den Start, führt den Job aus, schickt die letzten Zeilen der Ausgabe zusammen mit dem Exit-Code und gibt den Exit-Code am Ende weiter. So bleibt das Verhalten für Cron unverändert.

Laravel, systemd und Container

Viele moderne Anwendungen definieren geplante Aufgaben nicht direkt in der Crontab. Das Prinzip bleibt gleich:

  • Laravel Scheduler: Laravel bringt mit pingBefore(), thenPing(), pingOnSuccess() und pingOnFailure() eigene Methoden mit, um URLs vor und nach einer geplanten Aufgabe aufzurufen. Du kannst also für jede Aufgabe einen eigenen Monitor anlegen. Überwache zusätzlich den Scheduler selbst, denn wenn schedule:run nicht mehr aufgerufen wird, läuft keine einzige Aufgabe.
  • systemd-Timer: Hänge den Ping per ExecStartPost= an oder rufe ihn am Ende des Skripts auf, das der Service startet.
  • Kubernetes CronJobs und Container: Baue den Ping in den Entrypoint des Jobs ein. Gerade in Container-Umgebungen ist ein externer Heartbeat wertvoll, weil fehlgeschlagene Pods schnell aufgeräumt werden und mit ihnen die Logs.

Welche Jobs du zuerst überwachen solltest

Du musst nicht jeden Job auf einmal absichern. Fang mit denen an, deren Ausfall am längsten unbemerkt bleibt und am meisten kostet:

  1. Backups. Ein Backup, das seit Wochen nicht läuft, bemerkst du sonst genau dann, wenn du es brauchst.
  2. Datenimporte und Synchronisationen. Produktdaten, Lagerbestände, Wechselkurse oder CRM-Abgleiche.
  3. Abrechnung und E-Mail-Versand. Rechnungsläufe, Mahnungen, Newsletter.
  4. Aufräumjobs. Log-Rotation, das Löschen alter Sessions oder temporärer Dateien. Fallen sie aus, läuft irgendwann die Festplatte voll.
  5. Zertifikats-Erneuerung. Wenn ein Erneuerungsjob still scheitert, merkst du es erst, wenn das Zertifikat abläuft.

Für Agenturen, die viele Kundenprojekte betreuen, empfehlen wir eine klare Benennung der Monitore, etwa kunde-projekt-job. So erkennst du in einer Benachrichtigung sofort, wen es betrifft.

Häufige Fehler beim Einrichten

  • Den Ping an den Anfang setzen. Ein Heartbeat, der beim Start gesendet wird, sagt nichts über den Erfolg aus. Nutze dafür das Start-Signal und setze den eigentlichen Erfolgs-Ping ans Ende.
  • Kein Timeout beim Ping. Ohne -m kann ein hängender Netzwerk-Request deinen Job aufhalten.
  • Zu knappe Toleranzzeit. Wenn du regelmäßig Fehlalarme bekommst, wirst du sie irgendwann ignorieren. Passe die Toleranz an die tatsächlichen Laufzeiten an.
  • Ein Monitor für mehrere Jobs. Jeder Job braucht seine eigene Ping-URL, sonst verdeckt ein erfolgreicher Job den Ausfall eines anderen.
  • Die Ping-URL öffentlich machen. Behandle den Token wie ein Passwort. Wer ihn kennt, kann Erfolgsmeldungen senden. Lege ihn nicht in öffentlichen Repositories ab, sondern in Umgebungsvariablen.

Benachrichtigungen, die ankommen

Ein Alarm hilft nur, wenn er die richtige Person erreicht. Leite Cron-Alarme dorthin, wo dein Team ohnehin arbeitet. Kaspar benachrichtigt dich per E-Mail, Slack oder Telegram, wenn ein Ping ausbleibt, ein Job einen Fehler meldet oder die maximale Laufzeit überschritten wird, und meldet sich erneut, sobald der Job wieder normal läuft.

Fazit

Cronjobs sind unauffällig, solange sie laufen, und leider auch dann, wenn sie es nicht tun. Heartbeat-Monitoring macht aus dieser Stille ein Signal: Bleibt der Ping aus, weißt du Bescheid, egal aus welchem Grund. Mit einer Ping-URL pro Job, einer passenden Toleranzzeit und dem Exit-Code als Ergebnis hast du deine wichtigsten Jobs in wenigen Minuten abgesichert.

In Kaspar legst du dafür einen Cron-Monitor an, hinterlegst Intervall oder Cron-Ausdruck und kopierst die Ping-URL in deine Crontab. Uptime-Checks für deine Websites und Scans nach defekten Links laufen im selben Konto. Einen Überblick über die Tarife findest du auf unserer Preisseite.

  • Cron-Monitoring
  • Heartbeats
  • DevOps
  • Backups

Weitere Artikel