Dienste und systemd: Ladepfad, Abhängigkeiten, Neustarts
Du änderst eine Unit-Datei, startest den Dienst neu — und nichts ist anders. Ein Dienst läuft tadellos und ist nach dem Reboot verschwunden. Ein anderer startet zu früh, weil sein Mount noch nicht da war, und ein sleep 30 im Startbefehl macht den Fehler nur seltener. Alle drei sind dieselbe Ursache: systemd trennt Dinge, die im Alltag zusammenzugehören scheinen. Dieses Modul zeigt dir, welche Trennungen das sind — Unit gegen Anwendung, eingeschaltet gegen laufend, Anforderung gegen Reihenfolge — und wie du eine Dienständerung so durchführst, dass sie wirkt und ein Paketupdate überlebt.
Lehrtext · 12 Abschnitte · zuletzt geprüft: 2026-09-03
Ein Dienst ist eine Unit, kein Startskript
Früher war ein Dienst ein Shell-Skript, das man mit start und stop aufrief. Heute beschreibst du stattdessen einen gewünschten Zustand in einer Textdatei, und systemd sorgt dafür, dass er eintritt: Es startet den Prozess, überwacht ihn, startet ihn bei Bedarf neu, sammelt seine Ausgabe ein und kennt seine Beziehungen zu anderen Diensten.
Unit — Die Beschreibung eines Dings, das systemd verwaltet — ein Dienst, ein Timer, ein Mountpunkt, ein Socket. Der Dateiname endet auf den Typ, etwa nginx.service oder backup.timer.
Der Unterschied ist nicht kosmetisch. Ein Skript tut etwas, wenn man es aufruft. Eine Unit beschreibt etwas, und systemd leitet daraus ab, was zu tun ist. Fast alle Überraschungen im Umgang mit systemd kommen daher, dass jemand die Datei wie ein Skript behandelt: geändert, aufgerufen, und sich gewundert, dass nichts anders ist.
.service- Ein Prozess, den systemd startet und überwacht. Der häufigste Typ.
.timer- Ein zeitgesteuerter Auslöser, der eine
.service-Unit startet — die systemd-Entsprechung zu einem Cron-Eintrag. .socket- Ein Socket, den systemd öffnet und erst bei der ersten Verbindung den zugehörigen Dienst startet.
.mount- Ein Einhängepunkt. Auch Einträge aus
/etc/fstabwerden intern zu solchen Units. .target- Ein Sammelpunkt ohne eigenen Prozess, an dem sich andere Units aufhängen — der Ersatz für die alten Runlevel.
Wo Unit-Dateien liegen — und wer gewinnt
Unit-Dateien liegen an mehreren Orten, und die Reihenfolge entscheidet. Eine Datei aus einem früher gelisteten Verzeichnis überschreibt eine gleichnamige aus einem späteren:
/etc/systemd/system/- Deine Änderungen. Gewinnt gegen alles andere.
/run/systemd/system/- Zur Laufzeit erzeugt, verschwindet beim Neustart.
/usr/lib/systemd/system/- Was die Pakete mitbringen. Hier änderst du nichts — ein Paketupdate überschreibt es.
Daraus folgt die praktische Regel: Die Unit-Datei aus dem Paket lässt du in Ruhe. Willst du etwas ändern, legst du daneben eine Ergänzung an. Systemd liest dafür ein Verzeichnis mit dem Namen der Unit und der Endung .d; alle .conf-Dateien darin werden nach der Hauptdatei eingelesen und mit ihr verschmolzen. So änderst du einzelne Einstellungen, ohne die Originaldatei anzufassen.
systemctl edit <unit> erledigt genau das: Es öffnet einen Editor auf so einer Ergänzungsdatei, schreibt sie an die richtige Stelle und lädt die Konfiguration danach neu. Der Nebeneffekt ist der eigentliche Gewinn — die Änderung überlebt jedes Paketupdate, und systemctl cat <unit> zeigt dir hinterher Hauptdatei und Ergänzungen zusammen.
Warum deine Änderung nicht wirkt: daemon-reload
Hier ist die Stolperstelle, über die jeder einmal fällt. Du änderst eine Unit-Datei, startest den Dienst neu — und nichts ist anders.
Der Grund: systemd hält alle Units samt ihrer Abhängigkeiten im Speicher. Ein Neustart des Dienstes startet den Prozess neu, liest aber die Unit-Datei nicht erneut ein. Dafür gibt es einen eigenen Befehl: systemctl daemon-reload lädt die Manager-Konfiguration neu, liest alle Unit-Dateien erneut ein und baut den kompletten Abhängigkeitsbaum neu auf.
systemctl daemon-reload. Anwendungskonfiguration geändert → systemctl reload oder restart des Dienstes. Wer nur das eine tut, wundert sich über das andere.systemctl edit nimmt dir das ab — es lädt nach dem Speichern selbst neu. Wer die Datei dagegen von Hand anlegt, muss selbst daran denken. Neuere systemd-Versionen warnen inzwischen, wenn eine Unit-Datei auf der Platte neuer ist als die eingelesene Fassung; verlassen solltest du dich darauf nicht.
„enabled“ und „active“ sind zwei verschiedene Dinge
Die zweite klassische Verwechslung. systemctl start bringt den Dienst jetzt zum Laufen. systemctl enable sorgt dafür, dass er beim Booten startet — es hängt die Unit an den vorgesehenen Stellen ein, mehr nicht.
Die beiden sind voneinander unabhängig: Eine Unit kann eingeschaltet sein, ohne zu laufen, und laufen, ohne eingeschaltet zu sein. Genau daraus entstehen die zwei häufigsten Fehlerbilder — „läuft, ist aber nach dem Reboot weg“ und „startet nach dem Reboot, obwohl wir ihn abgeschaltet hatten“.
systemctl start/stop- Wirkt sofort, überlebt keinen Neustart des Systems.
systemctl enable/disable- Wirkt beim nächsten Boot, ändert am laufenden Zustand nichts.
systemctl enable --now- Beides zusammen — meistens das, was gemeint war.
systemctl is-active/is-enabled- Fragt die beiden Zustände getrennt ab. Für Skripte besser als der Text aus
status.
Kurzcheck
Ein Dienst läuft und arbeitet korrekt. Nach dem nächsten Reboot ist er weg. systemctl status hatte vorher „active (running)“ gezeigt. Was war die Ursache?
- Er war gestartet, aber nicht eingeschaltet — beim Booten hat ihn niemand aufgerufen.
- Der Dienst ist beim Booten abgestürzt und wurde deshalb aus dem Zielzustand entfernt.
- Die Unit-Datei lag unter
/run/systemd/system/und ist beim Neustart verloren gegangen.
Treffer. Genau. active beschreibt das Jetzt, enabled das Verhalten beim Booten. systemctl is-enabled hätte es vorher gezeigt; enable --now setzt beides.
Wann gilt ein Dienst als gestartet?
systemd muss entscheiden, ab wann ein Dienst „oben“ ist — davon hängt ab, wann nachfolgende Units starten dürfen. Diese Entscheidung steuert Type=, und eine falsche Angabe erzeugt Fehler, die wie Timing-Zufall aussehen.
Type=simple- Die Voreinstellung. Der Dienst gilt sofort als gestartet, nachdem der Prozess abgespalten wurde — noch bevor das Programm überhaupt läuft.
Type=exec- Wie
simple, aber systemd wartet, bis die Programmdatei tatsächlich ausgeführt wurde. Type=notify- Der Dienst meldet selbst, dass er bereit ist. Erst danach starten nachfolgende Units. Die ehrlichste Variante, wenn das Programm sie beherrscht.
Type=forking- Für Programme, die sich in den Hintergrund abspalten: Der gestartete Prozess beendet sich, wenn der Start fertig ist, und das Kind läuft weiter.
Type=simple gilt ein Dienst als gestartet, bevor er irgendetwas getan hat. Braucht ein anderer Dienst ihn wirklich — etwa eine Datenbank, die erst nach Sekunden Verbindungen annimmt —, dann startet der andere zu früh. Das Symptom sieht nach Netzwerkproblem aus und ist eine falsche Type=-Angabe.Abhängigkeit ist nicht Reihenfolge
Das ist der wichtigste Satz in diesem Modul, und er überrascht fast jeden: systemd trennt die Frage „was brauche ich“ von der Frage „in welcher Reihenfolge“. Beides wird getrennt angegeben, und keines impliziert das andere.
Requires=- Harte Anforderung: Die genannte Unit wird mitgestartet. Scheitert sie — und ist zusätzlich eine Reihenfolge gesetzt —, startet diese Unit nicht.
Wants=- Weiche Anforderung: Die genannte Unit wird mitgestartet, aber ihr Scheitern hat keine Folgen für diese Unit.
After=/Before=- Nur Reihenfolge. Sagt nichts darüber aus, ob die andere Unit überhaupt gestartet wird.
Requires= ohne After= startet beide Units gleichzeitig — die Anforderung ist erfüllt, die Reihenfolge war nie gefordert. Genau das erzeugt Fehler, die „meistens funktionieren“.Ein Dienst, der seinen Mountpunkt manchmal nicht findet
Szenario
Eine Anwendung schreibt nach /srv/data, einem eigenen Mount. Beim Booten schlägt der Start gelegentlich fehl, weil das Verzeichnis noch leer ist. Ein sleep 30 im Startbefehl macht den Fehler selten, aber nicht weg.
Anforderungen
- Der Dienst startet nur, wenn der Mount vorhanden ist
- Er startet erst, nachdem der Mount fertig ist
- Kein Raten über Zeiten
Schritte
- Die Abhängigkeit benennen: Die Anwendung braucht die Mount-Unit — das ist eine Anforderung, kein Zeitproblem.
- Die Reihenfolge zusätzlich angeben. Ohne sie startet systemd beide gleichzeitig, und der alte Fehler bleibt.
- Beides als Ergänzung über
systemctl editsetzen, damit ein Paketupdate es nicht überschreibt. systemctl daemon-reload— beziehungsweise darauf verlassen, dassedites selbst tut.- Den Bootpfad testen, nicht nur den Dienststart: Ein Neustart des Dienstes im laufenden System beweist nichts über die Reihenfolge beim Booten.
Merksatz: Eine Wartezeit rät über Verfügbarkeit. Eine deklarierte Abhängigkeit plus Reihenfolge beschreibt sie — und hält auch, wenn das System langsamer bootet als heute.
Wenn ein Dienst immer wieder neu startet
Restart= legt fest, ob systemd einen Dienst neu startet, wenn sein Prozess sich beendet, getötet wird oder in eine Zeitüberschreitung läuft. Die üblichen Werte sind on-failure — nur bei Fehlern — und always.
Damit ein kaputter Dienst das System nicht mit Startversuchen beschäftigt, gibt es eine Ratenbegrenzung: Startet eine Unit innerhalb eines Zeitfensters öfter als erlaubt, hört systemd auf und lässt sie im Zustand failed liegen. Die Grenze setzen StartLimitIntervalSec= und StartLimitBurst=.
systemctl reset-failed <unit> setzt den failed-Zustand zurück und dabei auch den Zähler der Startversuche auf null. Erst danach ist ein Start wieder möglich.Und eine Warnung zu Restart=always: Es macht ein System stabil gegen zufällige Abstürze und unsichtbar gegen systematische. Ein Dienst, der alle vier Minuten abstürzt und sofort wieder hochkommt, sieht im Monitoring aus wie ein laufender Dienst. Die Ratenbegrenzung ist deshalb kein Ärgernis, sondern das Signal, das den Unterschied sichtbar macht.
Reload statt Restart, wenn es geht
Ein Neustart unterbricht den Dienst. Viele Programme können ihre Konfiguration stattdessen im laufenden Betrieb neu einlesen — meistens ausgelöst durch ein Signal. In der Unit steht dafür ExecReload=, der Befehl, mit dem systemd genau das anstößt.
Die Einschränkung ist wichtig: Ein Reload wirkt nur auf das, was das Programm beim Neueinlesen tatsächlich übernimmt. Geöffnete Ports, gewechselte Benutzer und alles, was beim Start einmalig passiert, ändert ein Reload nicht. Und — das ist die häufigste Enttäuschung — an einer geänderten Unit-Datei ändert er ohnehin nichts; die liegt eine Ebene höher.
- Anwendungskonfiguration geändert
systemctl reloadversuchen, sonstrestart. Keindaemon-reloadnötig.- Unit-Datei oder Ergänzung geändert
systemctl daemon-reload, danach je nach Änderungrestart.- Beides geändert
- Erst
daemon-reload, dann die zur Anwendungsänderung passende Aktion.
Grenzen für den Dienst: Ressourcen und Sandbox
Weil systemd jeden Dienst in einer eigenen Cgroup führt, kann es ihm auch Grenzen setzen — und zwar ohne Änderung am Programm. Das ist einer der stärksten Hebel im ganzen System und wird selten genutzt.
Auf der Ressourcenseite lassen sich Speicher- und CPU-Anteile begrenzen. Wichtiger für die Sicherheit ist die Sandbox-Seite, weil sie die Schadenswirkung einer Lücke im Dienst einschränkt:
ProtectSystem=- Hängt
/usrund die Bootloader-Verzeichnisse schreibgeschützt ein. Mitfullzusätzlich/etc; mitstrictdie gesamte Hierarchie außer/dev,/procund/sys. ProtectHome=- Macht
/home,/rootund/run/userfür den Dienst unzugänglich und leer — oder wahlweise nur schreibgeschützt. PrivateTmp=- Gibt dem Dienst ein eigenes
/tmpund/var/tmp, isoliert von allen anderen. NoNewPrivileges=- Verhindert, dass der Prozess und seine Kinder je zusätzliche Rechte erlangen — etwa über setuid-Bits oder Dateisystem-Capabilities.
ProtectSystem=strict macht alles schreibgeschützt — ein Dienst, der seinen Zustand irgendwohin schreibt, scheitert danach mit einem Rechtefehler an einem Pfad, an dem die Rechte völlig in Ordnung aussehen. Solche Einstellungen gehören einzeln gesetzt und einzeln getestet, nicht als Block aus einem Blogartikel kopiert.Eine Unit für viele Instanzen
Wenn derselbe Dienst mehrfach laufen soll — pro Kunde, pro Port, pro Datenverzeichnis —, legt man dafür keine fünf fast identischen Dateien an. systemd kennt Vorlagen: Der Dateiname trägt ein @ vor der Endung, und beim Starten gibt man den Teil an, der sich unterscheidet.
Template-Unit — Eine Unit-Datei wie app@.service, aus der systemd bei Bedarf konkrete Instanzen erzeugt — etwa app@kunde1.service. Innerhalb der Datei steht der Instanzname als Platzhalter zur Verfügung, sodass eine Vorlage viele Dienste beschreibt.
Der Gewinn ist derselbe wie bei jeder Vorlage: eine Stelle zum Ändern statt fünf, und keine Chance, dass vier Kopien gepflegt werden und eine nicht. Der Preis ist ein Stück Indirektion — wer die Datei liest, sieht nicht sofort, welche Instanzen es tatsächlich gibt, und muss dafür die aktiven Units auflisten.
Nachsehen, was wirklich gilt — Journal und Unit-Sicht
Was ein Dienst auf die Standardausgabe schreibt, landet standardmäßig im Journal — er muss sich also nicht selbst um Logdateien kümmern. Abgefragt wird das mit journalctl -u <unit>, und genau diese Einschränkung auf die Unit ist der Unterschied zwischen einer Fehlersuche und dem Lesen einer Zeitleiste des ganzen Systems.
Praktisch relevant: systemctl status zeigt dir nur die letzten Zeilen. Für alles darüber hinaus brauchst du das Journal mit Unit-Filter und Zeitfenster — und für einen Dienst, der beim Booten scheitert, den Blick auf den vorherigen Bootvorgang.
Weil die wirksame Konfiguration aus Paketdatei plus Ergänzungen entsteht, ist „ich habe in die Datei geschaut“ kein Beleg mehr. Vier Werkzeuge beantworten die Fragen, die dabei entstehen:
systemctl cat <unit>- Zeigt Hauptdatei und alle Ergänzungen hintereinander — die Sicht, die der Wahrheit am nächsten kommt.
systemctl show <unit>- Zeigt die ausgewerteten Werte, wie systemd sie tatsächlich benutzt. Nützlich, wenn eine Einstellung ignoriert zu werden scheint.
systemd-analyze verify <datei>- Prüft eine Unit-Datei auf unbekannte Abschnitte und Direktiven, fehlende Abhängigkeiten und Startbefehle, die es gar nicht gibt oder die nicht ausführbar sind.
systemd-analyze critical-chain- Zeigt die zeitkritische Kette beim Booten als Baum — welche Unit auf welche gewartet hat und wie lange.
Besonders systemd-analyze verify ist unterschätzt: Es findet einen Tippfehler in einem Direktivennamen, bevor er dich im Betrieb überrascht. Eine falsch geschriebene Einstellung ist sonst nämlich kein Fehler — systemd ignoriert sie einfach, der Dienst startet, und die vermeintlich gesetzte Grenze existiert nie.
systemctl status für den groben Zustand, dann journalctl -u <unit> mit Zeitfenster für die eigentliche Meldung, dann systemctl cat für die wirksame Konfiguration. Wer in dieser Reihenfolge vorgeht, hat die Ursache meistens vor dem dritten Schritt gefunden.Die Reihenfolge, in der du eine Dienständerung durchführst
Requires= und After= sind orthogonal, du brauchst meistens beide. Und Paketdatei gegen Ergänzung — was du unter /etc ablegst, überlebt das nächste Update.Jetzt anwenden
Diesen Stoff gibt es als Modul mit bewerteten Entscheidungs-Checks — dieselbe Einführung, danach die Übungen.
Zum Modul →Quellen & Aktualität7 Primärquellen · zuletzt geprüft:
- 01man7.org/linux/man-pages/man5/systemd.unit.5.html
- 02man7.org/linux/man-pages/man5/systemd.service.5.html
- 03man7.org/linux/man-pages/man1/systemctl.1.html
- 04man7.org/linux/man-pages/man5/systemd.exec.5.html
- 05man7.org/linux/man-pages/man5/systemd.resource-control.5.html
- 06man7.org/linux/man-pages/man1/journalctl.1.html
- 07man7.org/linux/man-pages/man1/systemd-analyze.1.html