systemd verstehen – Services, Timer und Logs auf Linux

systemd-Services erstellen, verwalten und debuggen: Unit-Dateien schreiben, Dienste automatisch starten, mit journalctl Logs lesen und systemd Timer als Cron-Ersatz nutzen.

7 min Lesezeit

systemd verstehen – Services, Timer und Logs auf Linux

systemd ist das Init-System aller großen Linux-Distributionen. Es startet das System, verwaltet Dienste, schreibt Logs und plant Tasks. Wer systemd versteht, hat die volle Kontrolle über sein Linux-System.

Dieser Guide erklärt, wie systemd funktioniert, wie du eigene Services erstellst und wie du Logs mit journalctl effektiv auswertest.


systemd-Grundkonzepte: Units und Targets

systemd verwaltet Units – abstrakte Bausteine des Systems. Jede Unit ist eine Konfigurationsdatei.

Unit-Typen

Typ Dateiendung Zweck
Service .service Daemon/Dienst starten
Timer .timer Geplante Ausführung
Socket .socket Socket-Aktivierung
Mount .mount Dateisysteme einhängen
Target .target Gruppe von Units (wie Runlevel)
Path .path Datei-/Verzeichnisänderungen überwachen

Unit-Dateipfade

/usr/lib/systemd/system/     ← vom Paketmanager installierte Units (nicht ändern!)
/etc/systemd/system/         ← eigene und überschriebene Units (hier arbeiten!)
~/.config/systemd/user/      ← User-Units (ohne root)

Targets (früher: Runlevel)

# Aktuelles Target anzeigen
systemctl get-default
# graphical.target (Desktop-System)
# multi-user.target (Server ohne GUI)

# Verfügbare Targets
systemctl list-units --type=target

systemctl: Dienste verwalten

Die wichtigsten Befehle

# Status eines Dienstes
sudo systemctl status nginx

# Starten / Stoppen / Neu starten
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

# Neu laden ohne Unterbrechung (wenn der Dienst es unterstützt)
sudo systemctl reload nginx

# Aktivieren (beim Systemstart automatisch starten)
sudo systemctl enable nginx

# Deaktivieren
sudo systemctl disable nginx

# Aktivieren UND sofort starten (in einem Schritt)
sudo systemctl enable --now nginx

# Deaktivieren UND sofort stoppen
sudo systemctl disable --now nginx

Überblick über alle Services

# Alle aktiven Units anzeigen
systemctl list-units

# Nur Services
systemctl list-units --type=service

# Alle Services (auch inaktive)
systemctl list-units --type=service --all

# Fehlgeschlagene Units
systemctl --failed

# Unit-Datei anzeigen
systemctl cat nginx.service

# Abhängigkeiten anzeigen
systemctl list-dependencies nginx.service

System-Befehle

# System neu starten
sudo systemctl reboot

# Herunterfahren
sudo systemctl poweroff

# In Rescue-Modus wechseln (Single User)
sudo systemctl rescue

# systemd selbst neu laden (nach Unit-Datei-Änderungen)
sudo systemctl daemon-reload

Wichtig: Nach dem Erstellen oder Ändern einer Unit-Datei immer sudo systemctl daemon-reload ausführen!


journalctl: Logs lesen und filtern

systemd schreibt alle Service-Logs in das Journal – eine strukturierte, binäre Log-Datenbank. journalctl ist das Auswertungstool.

Basis-Befehle

# Alle Logs (neuste zuletzt)
journalctl

# Neuste Logs zuerst
journalctl -r

# Letzte 50 Zeilen
journalctl -n 50

# Logs live verfolgen (wie tail -f)
journalctl -f

# Logs eines bestimmten Dienstes
journalctl -u nginx
journalctl -u nginx -f   # Live
journalctl -u nginx -n 100  # Letzte 100 Zeilen

Zeitbasiertes Filtern

# Logs seit heute
journalctl --since today

# Logs der letzten Stunde
journalctl --since "1 hour ago"

# Logs in Zeitraum
journalctl --since "2026-02-18 10:00" --until "2026-02-18 12:00"

# Logs seit letztem Boot
journalctl -b

# Logs des vorletzten Boots
journalctl -b -1

# Alle verfügbaren Boots anzeigen
journalctl --list-boots

Priorität filtern

# Nur Fehler und kritisches
journalctl -p err

# Nur Warnungen und schlimmer
journalctl -p warning

# Prioritäten: emerg, alert, crit, err, warning, notice, info, debug

Ausgabeformate

# JSON-Format (für Scripting)
journalctl -u nginx -o json | head -5

# Kurzes Format (nur Nachricht)
journalctl -u nginx -o cat

# Verbose (alle Felder)
journalctl -u nginx -o verbose

Disk-Verbrauch

# Aktueller Journal-Speicherverbrauch
journalctl --disk-usage

# Altes Journal löschen (älter als 2 Wochen)
sudo journalctl --vacuum-time=2weeks

# Journal auf max. 500 MB begrenzen
sudo journalctl --vacuum-size=500M

Eigenen Service erstellen

Beispiel: Node.js-App als Service

Angenommen, deine App liegt in /opt/meine-app/ und wird mit node server.js gestartet.

# Service-Datei erstellen
sudo nano /etc/systemd/system/meine-app.service
[Unit]
Description=Meine Node.js Anwendung
Documentation=https://beispiel.de/docs
After=network.target        # Erst starten wenn Netzwerk verfügbar
Wants=network-online.target

[Service]
Type=simple
User=www-data               # Nicht als root laufen!
Group=www-data
WorkingDirectory=/opt/meine-app

# Umgebungsvariablen
Environment=NODE_ENV=production
Environment=PORT=3000
# Oder aus Datei laden:
# EnvironmentFile=/opt/meine-app/.env

# Startbefehl
ExecStart=/usr/bin/node /opt/meine-app/server.js

# Neustart bei Absturz
Restart=on-failure
RestartSec=5s
StartLimitIntervalSec=60s
StartLimitBurst=3

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=meine-app

[Install]
WantedBy=multi-user.target
# systemd informieren
sudo systemctl daemon-reload

# Aktivieren und starten
sudo systemctl enable --now meine-app

# Status prüfen
sudo systemctl status meine-app

# Logs prüfen
journalctl -u meine-app -f

Beispiel: Python/Gunicorn App

[Unit]
Description=Gunicorn für Django-App
After=network.target

[Service]
Type=notify
User=deploy
Group=deploy
WorkingDirectory=/opt/django-app
Environment=DJANGO_SETTINGS_MODULE=config.settings.production
EnvironmentFile=/opt/django-app/.env

ExecStart=/opt/django-app/.venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    --timeout 120 \
    config.wsgi:application

ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target

Beispiel: Einfaches Shell-Skript als Service

[Unit]
Description=Mein Backup-Skript
After=network.target

[Service]
Type=oneshot               # Läuft einmal durch, dann fertig
User=root
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Service-Typen und Optionen

Service-Typen (Type=)

Typ Verhalten
simple Prozess ist der Service (Standard)
forking Elternprozess forkt und beendet sich
oneshot Führt aus und beendet sich (für Skripte)
notify Service sendet Ready-Signal (Gunicorn, etc.)
idle Startet erst wenn andere Jobs fertig

Sicherheitsoptionen (empfohlen)

[Service]
# Dateisystem schützen
ProtectSystem=strict            # /usr, /boot read-only
ProtectHome=true                # /home, /root, /run/user nicht sichtbar
PrivateTmp=true                 # Eigenes /tmp-Verzeichnis

# Netzwerk einschränken (wenn kein Netzwerk benötigt)
PrivateNetwork=true

# Capabilities einschränken
NoNewPrivileges=true
CapabilityBoundingSet=

# Schreibzugriff nur auf bestimmte Verzeichnisse
ReadWritePaths=/opt/meine-app/data

Restart-Optionen

Restart=no              # Kein Neustart
Restart=always          # Immer neu starten
Restart=on-failure      # Nur bei Fehler
Restart=on-abnormal     # Bei Signal oder Timeout
RestartSec=5s           # Wartezeit vor Neustart

Ressourcenlimits

[Service]
# RAM-Limit
MemoryMax=512M

# CPU-Limit
CPUQuota=50%

# Maximale Dateien
LimitNOFILE=65535

# Maximale Prozesse
LimitNPROC=100

systemd Timer: Cron-Alternative

systemd Timer sind moderner als Cron: bessere Logging, Abhängigkeiten, Kalender-Ausdrücke.

Timer für Backup (täglich um 02:00 Uhr)

Service-Datei (/etc/systemd/system/backup.service):

[Unit]
Description=Tägliches Backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=root

Timer-Datei (/etc/systemd/system/backup.timer):

[Unit]
Description=Tägliches Backup um 02:00 Uhr

[Timer]
OnCalendar=*-*-* 02:00:00     # Täglich um 02:00
RandomizedDelaySec=300        # Bis zu 5 Min Zufallsverzögerung (verhindert Last-Spitzen)
Persistent=true               # Nachholen wenn System aus war

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

# Status des Timers
systemctl status backup.timer

# Alle aktiven Timer anzeigen
systemctl list-timers

# Timer manuell auslösen
sudo systemctl start backup.service

Kalender-Ausdrücke

OnCalendar=*-*-* 02:00:00     # Täglich um 02:00
OnCalendar=Mon *-*-* 00:00:00 # Montags um Mitternacht
OnCalendar=weekly              # Einmal pro Woche (Monntag 00:00)
OnCalendar=monthly             # Einmal pro Monat (1. um 00:00)
OnCalendar=*-*-* *:00:00       # Jede Stunde

# Zeitpunkt testen
systemd-analyze calendar "Mon *-*-* 02:00:00"

OnBootSec / OnActiveSec (relativ)

[Timer]
OnBootSec=5min          # 5 Minuten nach Boot
OnUnitActiveSec=1h      # Jede Stunde nach letzter Ausführung

Service-Debugging

Wenn ein Service nicht startet

# 1. Status anzeigen (zeigt oft direkt den Fehler)
sudo systemctl status meine-app

# 2. Logs der letzten Starts
journalctl -u meine-app --no-pager -n 50

# 3. Fehler beim letzten Start
journalctl -u meine-app -b --priority=err

# 4. Service manuell im Vordergrund ausführen (für Debugging)
sudo -u www-data /usr/bin/node /opt/meine-app/server.js

Syntax-Check einer Unit-Datei

# Unit analysieren (zeigt auch Warnungen)
systemd-analyze verify /etc/systemd/system/meine-app.service

systemd-Analyse

# Boot-Zeit analysieren
systemd-analyze

# Welche Services verlangsamen den Boot?
systemd-analyze blame

# Kritischen Pfad anzeigen
systemd-analyze critical-chain

Häufige Probleme

Unit not found

sudo systemctl status nicht-existiert
# Unit nicht-existiert.service could not be found.

# Datei existiert?
ls /etc/systemd/system/meine-app.service

# daemon-reload vergessen?
sudo systemctl daemon-reload

Service läuft, aber App antwortet nicht

# Prüfe ob der Prozess läuft
sudo systemctl status meine-app | grep PID
ps aux | grep node

# Port offen?
sudo ss -ltnp | grep :3000

EnvironmentFile nicht gefunden

meine-app.service: Failed to load environment files: /opt/meine-app/.env: No such file or directory
# Datei erstellen oder Pfad korrigieren
sudo nano /opt/meine-app/.env
sudo systemctl restart meine-app

Permission denied beim Start

# Benutzer prüfen (User= in [Service])
sudo systemctl cat meine-app | grep User

# Existiert der Benutzer?
id www-data

# Hat er Zugriff auf WorkingDirectory?
sudo -u www-data ls /opt/meine-app

Fazit

systemd ist mächtig, konsistent und gut dokumentiert. Die wichtigsten Takeaways:

  • systemctl enable --now → aktivieren und starten in einem Schritt
  • journalctl -u service -f → Logs live verfolgen
  • sudo systemctl daemon-reload → immer nach Unit-Datei-Änderungen!
  • Timer statt Cron für neue Tasks (bessere Logs, Dependencies)
  • Sicherheitsoptionen (PrivateTmp, ProtectSystem) immer setzen

War dieser Artikel hilfreich?