Ein systemd-Dienst, der nicht startet, ist fast nie geheimnisvoll. Führe systemctl status <unit> aus und lies die letzten 50 Journal-Zeilen dieser Unit mit journalctl -u <unit> -n 50 --no-pager — zwischen den beiden benennt einer von fünf Ursachen den Fall meist direkt: ein falscher Pfad, eine fehlende Binärdatei, falsche Berechtigungen, ein SELinux/AppArmor-Veto oder ein Syntaxfehler in der Unit-Datei. Der Grund steht im Log; die Fixes unten sind nur Mustererkennung dagegen.
Wie sehe ich, warum ein systemd-Dienst scheitert?
Erst der Status, dann das Journal:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager status gibt dir den Zustand (inactive (dead), failed (exit-code), activating (auto-restart)) und die letzten Log-Zeilen. Das Journal liefert die ganze Geschichte: stdout, stderr und systemds eigene Beschwerden über die Unit.
Schlug die Unit früher fehl und du willst den Datensatz dieses Laufs, ergänze -b für den aktuellen Boot oder --since today:
journalctl -u myapp.service -b --no-pager Für das volle Werkzeug — Boots, Prioritäten, Ausgabe live verfolgen — siehe das journalctl cheat sheet. Dasselbe Muskelgedächtnis.
Noch ein Paar, das man kennen sollte:
systemctl list-units --failed
systemctl reset-failed myapp.service Das erste listet jede rote Unit auf der Kiste. Das zweite räumt den failed-Zustand nach dem Fix weg — Kosmetik, aber es hört auf, dass Statusseiten Wolf schreien.
Was sind die häufigsten Ursachen?
Hat das Log das Symptom benannt, ist die Ursache fast immer eine dieser fünf:
| Log sagt | Wahrscheinliche Ursache | Fix |
|---|---|---|
status=203/EXEC | Falscher ExecStart=-Pfad oder fehlender Interpreter | Absoluter Pfad, chmod +x, Shebang prüfen |
status=203/EXEC bei einem Skript | Skript hat CRLF-Zeilenenden oder kaputten Shebang | dos2unix script.sh, erste Zeile richten |
status=1/FAILURE, keine App-Logs | Working Directory oder Umgebungsvariable fehlt | WorkingDirectory= setzen, Environment= ergänzen |
Permission denied | Nutzer darf Dateien nicht lesen oder den Port binden | Ownership richten; Ports unter 1024 brauchen AmbientCapabilities=CAP_NET_BIND_SERVICE oder root |
Unit is masked | Jemand hat systemctl mask ausgeführt | systemctl unmask myapp.service |
| Unit-Datei-Änderung „bewirkt nichts” | Daemon nicht neu geladen | systemctl daemon-reload |
Die 203/EXEC-Familie verdient eine Sondereinlage, weil sie Nachmittage frisst. Systemd startet ExecStart= nicht über deine Shell. Das heißt:
# Falsch — keine Shell, ~ wird nie aufgelöst, keine PATH-Suche
ExecStart=~/app/run.sh
# Richtig
ExecStart=/opt/app/run.sh Und das Skript selbst muss ausführbar sein und mit einem echten Shebang beginnen (#!/bin/bash oder #!/usr/bin/env bash). Ein Skript, das im Terminal läuft, aber unter systemd mit 203 scheitert, hat fast immer eins davon: nicht ausführbar, CRLF-Zeilenenden, ein Shebang ins Leere oder ein relativer Pfad.
Warum startet er manuell, aber nicht beim Boot?
Der klassische Reihenfolgen-Bug. Zeigt das Log Fehler direkt nach dem Boot, startet die Unit aber fein per Hand mit systemctl start, verliert dein Service ein Rennen — er greift nach Netzwerk, einer gemounteten Platte oder einer Datenbank, bevor das Ding existiert.
Der Fix: Abhängigkeiten deklarieren statt hoffen:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml After= ordnet den Start; Wants= bringt systemd dazu, die Abhängigkeit tatsächlich hochzufahren. network-online.target funktioniert nur, wenn der Network-Wait-Dienst auf deiner Distro aktiviert ist — prüfe systemctl is-enabled NetworkManager-wait-online.service (oder das systemd-networkd-Pendant). Der ExecStartPre-Wächter ist der billige, ehrliche Weg, laut mit einer lesbaren Nachricht zu scheitern statt mit einem Stack Trace.
Zweite Variante: Der Service startet beim Boot, stirbt aber sofort. Suche nach Dingen, die deine interaktive Umgebung hatte und der Boot nicht — PATH-Unterschiede, ein virtualenv, ein HOME. Setz explizit, was du brauchst:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app Warum schreibt mein Service nichts ins Journal?
Zeigt journalctl -u nichts, drei Checks der Reihe nach:
StandardOutput=undStandardError=in der Unit — sie müssenjournal(der Default) oderjournal+consolesein. Irgendwer hat sie womöglich aufnullgestellt.- Die App schreibt in eine Datei statt auf stdout. Systemd fängt nur stdout/stderr ab; Log-Datei-Schreiber umgehen das Journal komplett. Lenk die App auf stdout oder lies die Datei.
- Speicherlimits haben alte Zeilen fallen lassen:
journalctl --disk-usage, undSystemMaxUse=in/etc/systemd/journald.conf, wenn das Journal erstickt.
Für das Debugging des Starts selbst schlägt nichts, eine Shell in den Boot-Zeitkontext zu hängen:
[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1' Oder, für echte Mysterien: den exakten ExecStart=-Befehl als Unit-Nutzer in einer Shell laufen lassen — die meisten Umgebungsunterschiede zeigen sich in den ersten zehn Sekunden.
Wie kriegst du einen automatischen Neustart nach einem Crash hin?
Die Default-Policy ist Restart=no: Ein gecrashter Service bleibt tot, und du erfährst es von einem Nutzer. Pro Service fixen:
[Service]
Restart=on-failure
RestartSec=5 | Einstellung | Startet neu, wenn |
|---|---|
no (Default) | Nie |
on-failure | Exit-Code ungleich null, Signal, Timeout |
always | Jeder Exit, auch ein sauberer |
on-watchdog | Nur Watchdog-Timeout |
Restart=on-failure mit einer Startraten-Bremse paaren, damit eine Crash-Schleife nicht auf die Kiste hämmert: StartLimitIntervalSec= und StartLimitBurst= in der [Unit]-Sektion. Fünf Fehlschläge in sechzig Sekunden sollten einen Menschen rufen, keine CPU glühen lassen.
Wenn du das als geplanten Job statt als Daemon aufsetzt, wäge zuerst cron vs. systemd-Timer ab — Timer geben dir Journal-Logging und Abhängigkeits-Ordnung umsonst, genau das, worauf dieser Artikel ständig zugreift.
Die 60-Sekunden-Checkliste
systemctl status <unit>— Zustand und letzte Zeilen lesen.journalctl -u <unit> -n 50 --no-pager— den echten Fehler finden.203/EXEC? Pfad, Shebang, Berechtigungen fixen.Permission denied? Nutzer und File-Ownership richten.- Scheitert nur beim Boot?
After=/Wants=network-online.targetund einenExecStartPre-Wächter ergänzen. - Unit-Datei geändert?
systemctl daemon-reload && systemctl restart <unit>. Restart=on-failureergänzen, damit der nächste Crash sich meldet, statt sich zu verstecken.
Die meisten „systemd ist kompliziert”-Momente reduzieren sich auf der Fehler steckte die ganze Zeit im Journal. Lies es, bevor du die Unit-Datei editierst, nicht danach.
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?GGUF-Quantisierung: Welches Level solltest du nutzen?
Sie mögen die Artikel? Genau so baue ich beruflich. anheuern