Das Symptom
Ein Dienst ist nach einem Update kaputt. Das ist ärgerlich, aber verständlich. Nicht
verständlich ist, was danach passiert: Nach der Korrektur der Konfiguration
verweigert systemd den Start komplett. systemctl status zeigt den Zustand failed und
eine Zeile sinngemäß Start request repeated too quickly, dazu Failed with result 'start-limit-hit'. Kein Fehler des Programms, keine Ausgabe, einfach nichts. Auch
systemctl start ändert daran nichts. Minuten später, nach Kaffee und Ratlosigkeit,
geht es plötzlich wieder, ohne dass jemand etwas getan hätte.
Die „Layer 8“-Wahrheit
Die zweite Meldung ist keine zweite Störung, sondern eine Schutzvorrichtung. systemd
führt pro Unit einen Zähler, wie oft sie in einem Zeitfenster gestartet wurde.
systemd.unit(5) beschreibt die Regel: „Units which are started more than burst times
within an interval time span are not permitted to start any more.“ Die Vorgaben, wenn
die Unit nichts anderes sagt, stehen in systemd-system.conf(5): „DefaultStartLimitIntervalSec=
defaults to 10s. DefaultStartLimitBurst= defaults to 5.“ Fünf Starts in zehn
Sekunden, danach ist Schluss.
Dahin kommt ein Dienst schneller, als man die Fehlermeldung lesen kann. Mit
Restart=always oder on-failure startet systemd ihn nach jedem Abbruch neu, und die
Wartezeit RestartSec= steht laut systemd.service(5) auf 100ms. Ein Programm, das
wegen einer fehlerhaften Konfigurationszeile sofort beendet, wird also fünfmal
innerhalb einer halben Sekunde gestartet und dann gesperrt. Was im Journal darüber
steht, die eigentliche Fehlerausgabe, hat man in dem Moment meist noch nicht gelesen.
Der Teil, der die Ratlosigkeit erzeugt, steht im selben Absatz der Handbuchseite: Die
Begrenzung „apply to all kinds of starts (including manual), not just those triggered
by the Restart= logic.“ Deshalb bleibt auch der handgestartete Versuch wirkungslos,
obwohl die Konfiguration inzwischen stimmt. Und deshalb geht es Minuten später
wieder: Nach Ablauf des Intervalls darf die Unit laut Handbuchseite wieder manuell,
per Timer oder Socket gestartet werden, und die Restart-Logik ist von da an wieder
aktiv. Der Kaffee war nicht die Lösung, das Zeitfenster war abgelaufen.
Der direkte Weg führt über den Zähler. systemctl reset-failed setzt laut systemctl(1)
den Zustand failed zurück, und systemd.unit(5) ergänzt, dass der Befehl auch den
Zähler der Startratenbegrenzung löscht, genau für den Fall, dass ein Administrator
eine Unit von Hand starten will und das Limit im Weg steht. Danach startet der Dienst
sofort, vorausgesetzt, die Ursache ist wirklich behoben. Ist sie es nicht, steht man
nach fünf weiteren Abstürzen am selben Punkt.
Die Versuchung, das Limit mit StartLimitIntervalSec=0 abzuschalten, ist deshalb die
falsche Lehre aus dem Vorfall. Die Begrenzung existiert, damit ein defekter Dienst
nicht im Hundert-Millisekunden-Takt das Journal flutet. Wer Dienste betreibt, die beim
Hochfahren auf andere warten, gibt ihnen mit RestartSec= Luft und setzt das Fenster
mit StartLimitIntervalSec= und StartLimitBurst= bewusst, im Abschnitt [Unit], nicht
in [Service]. Dann bleibt die Sperre, was sie sein soll: ein Hinweis, dass etwas
fünfmal hintereinander schiefging, und nicht die sechste Störung.
Die echte, verifizierte Lösung
1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:
1.Die eigentliche Ursache im Journal lesen
Die Ratenbegrenzung ist das letzte Glied einer Kette. journalctl -u zeigt die Meldungen des Dienstes in Reihenfolge; die interessanten Zeilen stehen vor der Verweigerung: die Fehlerausgabe des Programms, ein fehlender Pfad, ein belegter Port, eine fehlende Konfigurationsdatei. systemctl status gibt den Exit-Code und den Zustand failed aus. Laut systemctl(1) bleibt dieser Zustand samt Exit-Code für den Administrator erhalten, bis die Unit gestoppt, neu gestartet oder per reset-failed zurückgesetzt wird.
systemctl status meindienst.service
journalctl -u meindienst.service -n 100 --no-pager
2.Die geltenden Grenzwerte dieser Unit anzeigen
systemctl show gibt die wirksamen Werte der Unit aus, egal ob sie aus der Unit-Datei, einem Drop-in oder den Manager-Vorgaben stammen. Sind in der Unit keine eigenen Werte gesetzt, erscheinen die Vorgaben aus systemd-system.conf(5), also zehn Sekunden und fünf Starts. Für die Manager-Vorgaben selbst lässt sich systemctl show ohne Unit-Namen aufrufen; auf dem Prüfsystem lieferte das DefaultStartLimitIntervalUSec=10s und DefaultStartLimitBurst=5.
systemctl show meindienst.service -p StartLimitIntervalUSec -p StartLimitBurst -p Restart -p RestartUSec
systemctl show -p DefaultStartLimitIntervalUSec -p DefaultStartLimitBurst
3.Ursache beheben, Zähler zurücksetzen, starten
Erst wenn der Grund für die Abstürze behoben ist, lohnt der Start. reset-failed setzt laut systemctl(1) den Zustand failed zurück und löscht laut systemd.unit(5) den Zähler der Startratenbegrenzung. Danach ist der Start wieder erlaubt. Wer den Zähler zurücksetzt, ohne die Ursache zu beheben, sieht innerhalb von Sekunden dieselbe Meldung wieder, diesmal mit fünf weiteren Absturzprotokollen im Journal.
systemctl reset-failed meindienst.service
systemctl start meindienst.service
4.Neustartverhalten passend einstellen
Für Dienste, die bei Abhängigkeiten wie einer Datenbank legitim mehrfach scheitern können, lohnt ein Drop-in. RestartSec= legt laut systemd.service(5) die Wartezeit vor dem Neustart fest und steht standardmäßig auf 100ms; ein paar Sekunden Pause nehmen dem Zähler den Druck. StartLimitIntervalSec= und StartLimitBurst= gehören in den Abschnitt [Unit]. Nach dem Anlegen des Drop-ins muss der Manager seine Konfiguration neu laden, sonst gilt weiter die alte Unit.
# /etc/systemd/system/meindienst.service.d/neustart.conf
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
RestartSec=5
# anschließend:
# systemctl daemon-reload
5.Beim Entwickeln eigener Dienste die Grenze einplanen
Wer einen eigenen Dienst mit Restart=always ausliefert, baut sich die Sperre selbst, wenn das Programm bei einem Konfigurationsfehler sofort beendet. systemd.service(5) nennt für Restart= die Werte no, on-success, on-failure, on-abnormal, on-watchdog, on-abort und always; die Voreinstellung ist no. Ein Dienst, der bei dauerhaft fehlerhafter Konfiguration abbricht, sollte das deutlich protokollieren und mit einem Exit-Code enden, bei dem ein Neustart keinen Sinn hat. Das erspart dem Administrator das Rätselraten, ob der Dienst kaputt ist oder nur gesperrt.