Linux System Administration

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.

UnitDie 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/fstab werden 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.

Der Preis dieser TrennungEine Einstellung kann jetzt an zwei Stellen stehen, und die wirksame Fassung sieht man in keiner der beiden Dateien allein. Wer eine Unit debuggt, muss sich angewöhnen, immer die zusammengeführte Sicht anzuschauen statt die Datei, die er zufällig geöffnet hat.

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.

MerksatzZwei getrennte Ebenen, zwei getrennte Befehle. Unit-Datei geändertsystemctl daemon-reload. Anwendungskonfiguration geändertsystemctl 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.
Die FalleBei 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.
Ausdrücklich orthogonalDie Dokumentation ist an dieser Stelle unmissverständlich: Reihenfolge-Einstellungen sind unabhängig von und orthogonal zu den Anforderungs-Einstellungen. Ein 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

  1. Die Abhängigkeit benennen: Die Anwendung braucht die Mount-Unit — das ist eine Anforderung, kein Zeitproblem.
  2. Die Reihenfolge zusätzlich angeben. Ohne sie startet systemd beide gleichzeitig, und der alte Fehler bleibt.
  3. Beides als Ergänzung über systemctl edit setzen, damit ein Paketupdate es nicht überschreibt.
  4. systemctl daemon-reload — beziehungsweise darauf verlassen, dass edit es selbst tut.
  5. 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=.

Der Zustand, der nach der Reparatur bleibtNachdem du die Ursache behoben hast, kann der Dienst trotzdem nicht starten — die Ratenbegrenzung greift noch. 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 reload versuchen, sonst restart. Kein daemon-reload nötig.
Unit-Datei oder Ergänzung geändert
systemctl daemon-reload, danach je nach Änderung restart.
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 /usr und die Bootloader-Verzeichnisse schreibgeschützt ein. Mit full zusätzlich /etc; mit strict die gesamte Hierarchie außer /dev, /proc und /sys.
ProtectHome=
Macht /home, /root und /run/user für den Dienst unzugänglich und leer — oder wahlweise nur schreibgeschützt.
PrivateTmp=
Gibt dem Dienst ein eigenes /tmp und /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.
Wie man sich damit selbst aussperrtDiese Direktiven sind wirksam und deshalb gefährlich, wenn man sie ungeprüft setzt. 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-UnitEine 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.

Der Reflex bei einem StartproblemErst 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

Feststellen, was du änderst: Anwendung oder UnitUnit-Änderungen als Ergänzung mit systemctl edit, nie in der PaketdateiBei Unit-Änderungen daemon-reload, sonst wirkt nichtsDie zur Änderung passende Aktion wählen: reload oder restartWirkung im Journal der Unit prüfen, nicht nur den Statustext
Was du mitnehmen solltestVier Trennungen tragen dieses Modul. Unit-Datei gegen Anwendungskonfiguration — zwei Ebenen, zwei Befehle. `enabled` gegen `active` — Booten gegen Jetzt, voneinander unabhängig. Anforderung gegen ReihenfolgeRequires= 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:
  1. 01man7.org/linux/man-pages/man5/systemd.unit.5.html
  2. 02man7.org/linux/man-pages/man5/systemd.service.5.html
  3. 03man7.org/linux/man-pages/man1/systemctl.1.html
  4. 04man7.org/linux/man-pages/man5/systemd.exec.5.html
  5. 05man7.org/linux/man-pages/man5/systemd.resource-control.5.html
  6. 06man7.org/linux/man-pages/man1/journalctl.1.html
  7. 07man7.org/linux/man-pages/man1/systemd-analyze.1.html