Linux System Administration

Zeit und Zeitpläne: Auslösung, Uhr, Nachweis

„Der Job läuft um drei“ enthält zwei Aussagen, die unabhängig voneinander falsch sein können: dass etwas ihn auslöst, und dass drei Uhr wirklich drei Uhr ist. Zweimal im Jahr läuft ein Cron-Job deshalb gar nicht oder doppelt — beides ist dokumentiertes Verhalten, kein Fehler. Dieses Modul zeigt dir, warum ein Skript im Terminal läuft und im Zeitplan nicht, was systemd-Timer zusätzlich können, wie du dreihundert Hosts davon abhältst, gleichzeitig loszulaufen, und warum eine Uhr im Betrieb gezogen und nicht gesprungen wird.

Lehrtext · 12 Abschnitte · zuletzt geprüft: 2026-09-03

Zwei Fragen, die sich als eine tarnen

„Der Job läuft um drei“ klingt nach einer einzigen Aussage. Tatsächlich stecken zwei darin, und sie können unabhängig voneinander falsch sein. Die erste lautet: Löst irgendetwas den Lauf zum vorgesehenen Zeitpunkt aus? Die zweite: Ist das, was der Host für „drei Uhr“ hält, auch drei Uhr?

Solange beides stimmt, fällt die Unterscheidung nicht auf. Sie fällt an dem Morgen auf, an dem der Abrechnungslauf zweimal in den Büchern steht, oder an dem Protokolleinträge von zwei Hosts sich gegenseitig überholen, obwohl der eine den anderen ausgelöst hat. Beides sind Zeitprobleme, aber sie haben verschiedene Ursachen und verschiedene Werkzeuge.

Dieses Modul behandelt beide Hälften: erst die Auslösung — Cron, systemd-Timer und die Fallen dazwischen — und dann die Uhr selbst, ihre Synchronisierung und die Frage, welche der beiden Uhren im System man für welche Messung nimmt.

Cron ist einfach — und genau deshalb überraschend

Cron prüft laut Dokumentation jede Minute seine Einträge und startet, was auf die aktuelle Zeit passt. Das ist das ganze Modell. Es hat keinen Zustand, keine Vorstellung davon, ob ein Lauf gelungen ist, und keine Erinnerung an einen versäumten Termin.

Die häufigste Überraschung ist keine Zeitfrage, sondern eine Umgebungsfrage. Ein Skript, das in deiner Sitzung läuft, findet dort Dutzende Umgebungsvariablen vor, die deine Anmeldung gesetzt hat. Cron setzt laut Dokumentation nur wenige davon: SHELL auf /bin/sh, HOME und LOGNAME aus dem Eintrag der Benutzerdatenbank. Der Suchpfad ist kurz.

  • Aufrufe ohne vollen Pfad schlagen fehl, weil /usr/local/bin nicht im Suchpfad steht
  • Bash-eigene Schreibweisen scheitern, weil /bin/sh nicht Bash ist
  • Zugangsdaten aus dem Anmeldeprofil fehlen, weil das Profil gar nicht gelesen wurde
  • Die Ausgabe landet nicht im Terminal, sondern als E-Mail beim Eigentümer des Eintrags
Prüfung„Im Terminal funktioniert es“ ist kein Test für einen Cron-Job. Der belastbare Test ist ein Aufruf mit einer bewusst leergeräumten Umgebung, etwa über env -i, oder ein erster Lauf, dessen vollständige Ausgabe man sich in eine Datei schreiben lässt.

Die zweite Überraschung betrifft das Auffinden. Cron-Einträge liegen an mehreren Stellen gleichzeitig: in der persönlichen Crontab jedes Kontos, in /etc/crontab, in einzelnen Dateien unter /etc/cron.d und — als Sonderform ohne Uhrzeit — als ausführbare Dateien in Verzeichnissen wie /etc/cron.daily. Wer einen unbekannten nächtlichen Lauf sucht, muss alle diese Orte kennen; ein Blick in die eigene Crontab beantwortet die Frage nicht.

Die Dateien unter /etc/cron.d und /etc/crontab haben dabei ein Feld mehr als eine persönliche Crontab: Zwischen Zeitangabe und Befehl steht dort das Konto, unter dem der Lauf stattfindet. Wird ein Eintrag von der einen Stelle an die andere kopiert, verschiebt sich alles um eine Spalte — und der Fehler zeigt sich nicht als Fehlermeldung, sondern als Lauf zu einer anderen Zeit.

Die Stunde, die zweimal kommt

Zweimal im Jahr verschiebt sich die lokale Zeit. Cron vergleicht die Einträge gegen die lokale Wanduhr, und daraus folgt ein Verhalten, das die Dokumentation ausdrücklich benennt: Zeiten, die es nicht gibt, treffen nie — der Job läuft in dieser Nacht gar nicht. Zeiten, die zweimal vorkommen, treffen zweimal — der Job läuft doppelt.

Der Abrechnungslauf, der einmal im Jahr doppelt bucht

Szenario

Ein Job schreibt um 02:30 Uhr die Tagesumsätze fort. Er ist nicht wiederholbar gebaut: Jeder Lauf addiert die Bewegungen des Vortages auf einen Saldo.

Anforderungen

  • Eintrag: 30 2 * * *
  • Zeitzone des Hosts: Europe/Berlin

Schritte

  1. Bei der Rückstellung im Herbst läuft die Uhr von 03:00 auf 02:00 zurück. Der Zeitpunkt 02:30 kommt in dieser Nacht zweimal vor.
  2. Cron trifft beide Male. Der Saldo enthält den Vortag doppelt.
  3. Bei der Umstellung im Frühjahr springt die Uhr von 02:00 auf 03:00. Den Zeitpunkt 02:30 gibt es an diesem Tag nicht — der Lauf fällt ersatzlos aus.
  4. Beide Fälle fallen erst in der Buchhaltung auf, Wochen später.

Merksatz: Die zuverlässige Abhilfe liegt nicht im Zeitplan, sondern im Job: Ein Lauf, der den Ist-Zustand liest und nur die Abweichung schreibt, überlebt beide Fälle. Wo das nicht geht, hilft ein Zeitpunkt außerhalb des Umstellungsfensters — oder ein Host, der in UTC rechnet.

Was systemd-Timer zusätzlich mitbringen

Ein Timer ist eine Unit, die eine andere Unit startet. Damit erbt der Lauf alles, was für Dienste ohnehin gilt: eine eigene Zeile im Journal, Abhängigkeiten zu anderen Units, Ressourcengrenzen, die Sandbox-Direktiven und einen Status, den man abfragen kann.

Nachvollziehbarkeit
systemctl list-timers zeigt den nächsten und den letzten Lauf; das Journal zeigt Ausgabe und Ergebnis jedes einzelnen.
Abhängigkeiten
Der Lauf kann warten, bis das Netz erreichbar oder ein Dateisystem eingehängt ist — Cron kennt diese Bedingungen nicht.
Begrenzung
Speicher- und CPU-Grenzen sowie eingeschränkte Dateisystemsicht gelten für den Job wie für jeden anderen Dienst.
Nachholen
Ein versäumter Lauf kann nachgeholt werden — dazu gleich mehr.

Der Preis ist Umfang: Aus einer Zeile Cron werden zwei Dateien, die Unit und der Timer. Für einen einzelnen Aufräumbefehl auf einem einzelnen Host ist das viel Zeremonie. Für einen Job, dessen Ausfall jemand bemerken soll, ist es die Voraussetzung dafür, dass man es überhaupt bemerken kann.

Der Zeitpunkt ist ein Fenster, kein Punkt

OnCalendar= legt den Zeitpunkt gegen die Wanduhr fest, etwa OnCalendar=*-*-* 03:00:00. Der Lauf beginnt aber nicht exakt dann. Die Dokumentation beschreibt ein Fenster: Es beginnt zum angegebenen Zeitpunkt und endet um AccuracySec= später, voreingestellt eine Minute. Innerhalb dieses Fensters liegt der tatsächliche Start an einer host-eigenen, zufälligen, aber stabilen Position.

Das ist eine Stromsparmaßnahme: Der Kernel kann mehrere anstehende Timer zusammenlegen und muss die CPU nur einmal aufwecken statt fünfmal. Für einen nächtlichen Aufräumlauf ist die Minute egal. Wo es auf Sekunden ankommt, setzt man den Wert herunter — die Dokumentation nennt 1us als feinste Einstellung.

VerwechslungsgefahrAccuracySec= und RandomizedDelaySec= sehen ähnlich aus und verfolgen laut Dokumentation entgegengesetzte Ziele. Die eine legt Aufwachvorgänge zusammen, um Strom zu sparen. Die andere zieht Läufe auseinander, um Lastspitzen zu vermeiden. Wer Spitzen glätten will und dafür die Genauigkeit lockert, bekommt beides nicht.

Der Lauf, der nachgeholt wird

Cron kennt versäumte Termine nicht: War der Host um drei aus, ist der Lauf verloren. Für einen Server, der durchläuft, ist das folgenlos. Für eine Maschine, die abends heruntergefahren wird, oder eine virtuelle Instanz, die nur zeitweise läuft, ist es der Normalfall.

Persistent=true speichert laut Dokumentation den Zeitpunkt des letzten Laufs auf der Platte. Wird der Timer aktiviert und hätte in der Zwischenzeit mindestens einmal ausgelöst, startet er sofort. Die Voreinstellung ist false, und die Einstellung wirkt nur auf Timer mit OnCalendar=.

FolgeBeim Hochfahren nach einem langen Ausfall lösen alle nachholenden Timer gleichzeitig aus — und zwar zusätzlich zu allem, was der Systemstart ohnehin tut. Genau hier gehört RandomizedDelaySec= dazu; die Dokumentation stellt ausdrücklich klar, dass die Verzögerung auch für nachgeholte Läufe gilt.

Dreihundert Hosts, ein Zeitpunkt

Steht auf dreihundert Hosts derselbe Eintrag für 03:00 Uhr, starten dreihundert Läufe gleichzeitig. Solange sie nur lokal aufräumen, fällt es nicht auf. Sobald sie ein gemeinsames Ziel ansprechen — einen Paketspiegel, eine Datenbank, ein Sicherungsziel —, ist der Zeitplan die Ursache eines selbstgemachten Lastschadens.

RandomizedDelaySec=1h verzögert jeden Lauf laut Dokumentation um einen zufällig gewählten, gleichverteilten Betrag zwischen null und dem angegebenen Wert. Aus einer Spitze wird eine Stunde gleichmäßiger Last. Voreingestellt ist 0, also keine Verzögerung.

Der Wert wird laut Dokumentation vor jedem Durchlauf neu ausgewürfelt — derselbe Host läuft also nicht jeden Tag zur selben Minute. Wer das nicht will, weil er die Läufe untereinander in eine feste Reihenfolge bringen muss, kann die Zufallszahl über FixedRandomDelay= an den Host binden.

Der Preis ist die Unschärfe: Ein Lauf mit einer Stunde Streuung passt in kein enges Wartungsfenster mehr, und die Fehlersuche wird umständlicher, weil man nicht mehr weiß, wann genau man ins Protokoll schauen muss. Für zusammenhängende Abläufe, die aufeinander warten, ist Streuung deshalb das falsche Mittel — dort gehört die Reihenfolge in Abhängigkeiten, nicht in Uhrzeiten.

Timer, die nicht auf die Wanduhr schauen

Neben Kalenderzeiten kennt systemd monotone Timer. Sie zählen nicht ab einem Datum, sondern ab einem Ereignis: OnBootSec= ab dem Start der Maschine, OnUnitActiveSec= ab dem letzten Start derselben Unit.

Der zweite löst ein Problem, das Kalenderpläne nicht lösen können. Ein Job, der laut Plan alle fünf Minuten startet, aber gelegentlich acht Minuten braucht, überschneidet sich mit sich selbst. OnUnitActiveSec=5min misst dagegen ab dem letzten Lauf: Der nächste beginnt fünf Minuten nachdem der vorige begonnen hat, und die Läufe können sich nicht aufstauen wie bei einem starren Raster.

Monotone ZeitEine Zeitmessung, die nur vorwärts läuft und von Korrekturen der Wanduhr unberührt bleibt. Sie beantwortet „wie lange ist es her“, nicht „welches Datum ist heute“.

Dieselbe Unterscheidung gilt im Programm. Ein Zeitlimit von 30 Sekunden, das aus zwei Wanduhr-Zeitstempeln gerechnet wird, kann durch eine Uhrkorrektur negativ werden oder ins Leere laufen. Dauern und Zeitlimits gehören an die monotone Uhr; Datumsangaben, Zertifikatsprüfungen und Protokollzeitstempel an die Wanduhr.

Uhren korrigiert man nicht mit einem Sprung

Jede Rechneruhr geht falsch; die Frage ist nur, wie schnell. Ein Synchronisierungsdienst gleicht das laufend aus, und dafür hat er zwei Möglichkeiten, die sich grundlegend unterscheiden.

Slew (ziehen)
Die Uhr wird vorübergehend etwas schneller oder langsamer gestellt, bis die Abweichung aufgebraucht ist. Die Zeit läuft dabei durchgehend vorwärts.
Step (springen)
Die Uhr wird auf den richtigen Wert gesetzt. Der Sprung kann rückwärts gehen — dann gibt es Zeitstempel, die zweimal vorkommen.

Chrony korrigiert laut Dokumentation im Normalbetrieb durch Ziehen. Springen ist die Ausnahme und wird über makestep an zwei Bedingungen geknüpft: Die Abweichung muss über einem Schwellwert liegen, und es darf seit dem Start des Dienstes nicht mehr als eine festgelegte Zahl von Aktualisierungen gegeben haben. Die Dokumentation nennt den Grund unmissverständlich: Auf den meisten Systemen soll nur beim Systemstart gesprungen werden, bevor Programme laufen, die auf monoton vorwärtslaufende Zeit angewiesen sind.

Ein Rückwärtssprung im laufenden Betrieb ist deshalb kein Betriebsdetail, sondern ein Zwischenfall: Protokolle verlieren ihre Reihenfolge, Zeitlimits laufen ins Leere, Datenbanken mit zeitbasierten Schlüsseln sehen Kollisionen, und Kerberos- oder Zertifikatsprüfungen können fehlschlagen. Wo eine große Abweichung im Betrieb auftritt, ist die Frage nicht, wie schnell man sie wegkorrigiert, sondern welche Anwendung den Sprung nicht verträgt.

Virtuelle Maschinen bringen dafür eine eigene Quelle mit. Wird ein Gast angehalten und später fortgesetzt — nach einem Schnappschuss, einer Verschiebung auf einen anderen Wirt, einem ausgesetzten Zustand —, ist seine Uhr um genau die Pause zurück. Der Gast hat währenddessen nichts gemessen; für ihn ist keine Zeit vergangen.

Zwei Instanzen wollen das dann gleichzeitig korrigieren: die Integration des Virtualisierers und der Synchronisierungsdienst im Gast. Arbeiten beide, ziehen sie die Uhr gegeneinander, und die Abweichung pendelt statt zu verschwinden. Deshalb gehört bei einer Zeitabweichung im Gast als Erstes geklärt, wer dort eigentlich die Uhr stellen soll — und die andere Seite abgeschaltet.

Zeitzone, Hardware-Uhr und die Zeit im Protokoll

Ein Host hat mehrere Zeitangaben gleichzeitig, und timedatectl zeigt sie nebeneinander: die lokale Zeit, die dazugehörige Zeitzone, die Zeit in UTC, den Stand der Hardware-Uhr und zwei Statuszeilen — ob eine Zeitsynchronisierung aktiv ist und ob die Systemuhr als synchronisiert gilt.

Die letzten beiden werden gern verwechselt. Ein laufender Dienst bedeutet nicht, dass er eine brauchbare Quelle erreicht hat. Beim Nachgehen einer Zeitabweichung ist deshalb die Zeile über die Synchronisierung die erste, die man liest — nicht die Uhrzeit selbst.

Die Hardware-Uhr läuft üblicherweise in UTC, und das System rechnet die lokale Zeit daraus aus. Steht sie stattdessen auf lokaler Zeit — ein Erbe aus Systemen, die sich einen Rechner mit einem anderen Betriebssystem teilen —, springt der Host bei jeder Zeitumstellung um eine Stunde, sobald er neu startet, und die Protokolle des Umstellungstages werden unbrauchbar.

Kurzcheck

Ein Host in Europe/Berlin schreibt Protokolle. Ein Auswertungssystem sammelt sie zusammen mit denen aus drei anderen Zeitzonen. Was ist die tragfähige Einstellung?

  • Zeitstempel in UTC schreiben und erst bei der Anzeige in die lokale Zeit umrechnen
  • Alle Hosts auf dieselbe lokale Zeitzone stellen, damit die Zeitstempel direkt vergleichbar sind
  • Die Zeitzone in jeden Zeitstempel schreiben und die Umrechnung dem Auswertungssystem überlassen

Treffer. Richtig. UTC kennt keine Umstellung und keine doppelten Stunden — die Reihenfolge über alle Quellen hinweg bleibt eindeutig. Die lokale Zeit ist eine Frage der Darstellung, nicht der Speicherung.

Ein Zeitplan ist keine Zusage über den Ablauf

Der Zeitplan sagt, wann etwas beginnt. Er sagt nichts darüber, ob der vorige Lauf fertig ist, ob der Lauf gelungen ist, oder ob er beim zweiten Mal dasselbe tut wie beim ersten. Diese drei Zusagen gehören in den Job, nicht in den Zeitplan.

  • Überschneidung: eine Sperre im Job, oder ein monotoner Timer, der ab dem letzten Lauf misst
  • Wiederholbarkeit: erst den Ist-Zustand lesen, dann nur die Abweichung ändern
  • Nachweis: ein Exitcode, der zwischen „nichts zu tun“ und „fehlgeschlagen“ unterscheidet
  • Überwachung: eine Prüfung auf den Zeitpunkt des letzten erfolgreichen Laufs, nicht nur auf Fehlermeldungen

Der letzte Punkt wird am häufigsten übersehen. Ein Job, der stillschweigend nicht mehr startet — weil der Timer deaktiviert wurde, weil die Unit einen Tippfehler hat, weil die Maschine neu aufgesetzt wurde —, erzeugt keine Fehlermeldung. Er erzeugt gar nichts. Auffällig wird er nur, wenn jemand das Ausbleiben überwacht.

Die Reihenfolge beim Nachgehen

Ist der Lauf ausgelöst worden? — systemctl list-timers oder das Protokoll des DienstesIst er ausgelöst worden und fehlgeschlagen? — Journal der ausgelösten Unit, ExitcodeStimmt die Zeit, gegen die geplant wird? — timedatectl, Zeile zur SynchronisierungWurde die Uhr korrigiert? — Sprünge im Protokoll des ZeitdienstesWar es die Umgebung statt der Zeit? — Suchpfad, Shell, fehlende Variablen

Die Reihenfolge ist bewusst so gewählt: Der erste Schritt trennt „lief nicht“ von „lief und ging schief“, und diese Trennung halbiert den Suchraum. Wer stattdessen mit der Anwendung beginnt, sucht oft stundenlang nach einem Fehler in einem Lauf, den es nie gegeben hat.

Jetzt anwenden

Diesen Stoff gibt es als Modul mit bewerteten Entscheidungs-Checks — dieselbe Einführung, danach die Übungen.

Zum Modul →
Quellen & Aktualität10 Primärquellen · zuletzt geprüft:
  1. 01man7.org/linux/man-pages/man5/crontab.5.html
  2. 02man7.org/linux/man-pages/man5/systemd.timer.5.html
  3. 03man7.org/linux/man-pages/man7/systemd.time.7.html
  4. 04chrony-project.org/doc/4.5/chrony.conf.html
  5. 05man7.org/linux/man-pages/man1/timedatectl.1.html
  6. 06man7.org/linux/man-pages/man8/cron.8.html
  7. 07freedesktop.org/software/systemd/man/latest/systemd.special.html
  8. 08man7.org/linux/man-pages/man1/systemctl.1.html
  9. 09freedesktop.org/software/systemd/man/latest/systemd.service.html
  10. 10man7.org/linux/man-pages/man2/clock_gettime.2.html