Zum Inhalt springen

Dienste steuern

systemctl ist der Befehl, mit dem du mit systemd sprichst. Ein Dutzend Unterbefehle deckt den Alltag ab. Auf dieser Seite probierst du sie an SSH und an einem frisch installierten Webserver aus und schreibst dann einen eigenen Dienst.

Befehl Wirkung
systemctl status NAME Zustand, PID, Speicherverbrauch und die letzten Logzeilen
sudo systemctl start NAME Jetzt starten
sudo systemctl stop NAME Jetzt stoppen
sudo systemctl restart NAME Stoppen und neu starten (Prozess wird beendet)
sudo systemctl reload NAME Konfiguration neu einlesen, ohne den Prozess zu beenden (nicht jeder Dienst kann das)
sudo systemctl enable NAME Beim Booten automatisch starten
sudo systemctl disable NAME Nicht mehr automatisch starten
systemctl is-active NAME Nur active oder inactive, ideal für Skripte
systemctl is-enabled NAME Nur enabled oder disabled

Das .service am Ende darfst du weglassen, systemctl status ssh und systemctl status ssh.service sind dasselbe. Lesende Befehle gehen ohne sudo, verändernde brauchen es.

Hands-on: mit SSH, dem Dienst, über den du gerade verbunden bist.

Terminal-Fenster
systemctl status ssh
systemctl is-active ssh
systemctl is-enabled ssh

Lies die Status-Ausgabe Zeile für Zeile: Loaded nennt die Unit-Datei und ob sie enabled ist, Active den Zustand und seit wann, Main PID den Prozess, darunter die letzten Logzeilen. Was passiert wohl, wenn du sudo systemctl stop ssh tippst, während du per SSH verbunden bist? Bestehende Verbindungen bleiben offen, aber neue sind nicht mehr möglich. Lass es besser bleiben.

Das ist die häufigste Verwechslung. start und stop wirken jetzt, enable und disable wirken beim nächsten Boot. Die beiden Achsen sind unabhängig:

flowchart LR
    subgraph jetzt["Jetzt"]
        direction TB
        start["systemctl start"] --> laeuft["läuft"]
        stop["systemctl stop"] --> steht["gestoppt"]
    end
    subgraph boot["Nach dem nächsten Boot"]
        direction TB
        enable["systemctl enable"] --> auto["startet automatisch"]
        disable["systemctl disable"] --> nicht["startet nicht"]
    end
enabled disabled
active Normalfall für einen Serverdienst Läuft nur bis zum nächsten Reboot
inactive Gestoppt, kommt nach dem Reboot wieder Aus, und bleibt aus

enable erzeugt lediglich einen Symlink in /etc/systemd/system/multi-user.target.wants/, disable entfernt ihn. Wer beides auf einmal will, nimmt sudo systemctl enable --now NAME beziehungsweise disable --now.

Terminal-Fenster
sudo apt update
sudo apt install -y nginx
systemctl status nginx
curl localhost

curl holt die Startseite des Webservers, du solltest HTML mit «Welcome to nginx!» sehen. Ubuntu-Pakete aktivieren und starten ihren Dienst bei der Installation gleich mit. Jetzt der Zyklus:

Terminal-Fenster
sudo systemctl stop nginx
curl localhost
sudo systemctl start nginx
curl -s localhost | head -n 4
sudo systemctl disable nginx
systemctl is-enabled nginx
sudo systemctl enable nginx

Bei gestopptem nginx meldet curl «Connection refused». Was zeigt ls -l /etc/systemd/system/multi-user.target.wants/ nach disable und nach enable?

Und der Unterschied zwischen restart und reload:

Terminal-Fenster
systemctl show nginx -p MainPID
sudo systemctl reload nginx
systemctl show nginx -p MainPID
sudo systemctl restart nginx
systemctl show nginx -p MainPID

Nach reload ist die PID gleich geblieben, nach restart ist es ein neuer Prozess. Für Konfigurationsänderungen ist reload darum der bessere Weg, falls der Dienst ihn anbietet.

Terminal-Fenster
systemctl cat nginx

systemctl cat zeigt die Unit-Datei mit ihrem Pfad. Das Format ist das klassische INI-Format mit drei Abschnitten:

/usr/lib/systemd/system/nginx.service (gekürzt)
[Unit]
Description=A high performance web server and a reverse proxy server
After=network.target
[Service]
Type=forking
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
[Install]
WantedBy=multi-user.target
Abschnitt Schlüssel Bedeutung
[Unit] Description= Text, der in status erscheint
After= Erst starten, wenn diese Unit gestartet ist (Reihenfolge, keine harte Abhängigkeit)
[Service] Type= simple (Standard, Prozess bleibt im Vordergrund), forking (Dienst legt sich selbst in den Hintergrund), oneshot (läuft einmal durch, dann fertig)
ExecStart= Der Befehl, absoluter Pfad zwingend
ExecReload= Was systemctl reload ausführt
Restart= no, on-failure oder always: automatisch neu starten?
User= Als welcher Benutzer der Dienst läuft (Standard: root)
[Install] WantedBy= In welches Target enable den Link legt

Die vollständige Liste steht in man systemd.service und man systemd.unit, die Manpages sind lang, aber gut.

Ein Dienst, der alle 10 Sekunden eine Zeile mit Datum in eine Datei schreibt. Nutzlos, aber vollständig, und die Vorlage für alles Spätere.

  1. Das Skript. Es endet nie, genau wie ein echter Dienst:
    /usr/local/bin/ticker.sh
    #!/usr/bin/env bash
    # ticker.sh: schreibt alle 10 Sekunden eine Zeile mit Zeitstempel
    set -euo pipefail
    logfile="/var/tmp/ticker.log"
    while true; do
    echo "$(date '+%F %T') tick" >> "$logfile"
    sleep 10
    done
    Anlegen mit sudo vim /usr/local/bin/ticker.sh, dann ausführbar machen und einmal von Hand testen:
    Terminal-Fenster
    sudo chmod 755 /usr/local/bin/ticker.sh
    /usr/local/bin/ticker.sh
    Nach ein paar Sekunden mit Ctrl+C abbrechen und cat /var/tmp/ticker.log ansehen. Erst wenn das Skript von Hand läuft, lohnt sich der Dienst.
  2. Die Unit-Datei:
    /etc/systemd/system/ticker.service
    [Unit]
    Description=Ticker: schreibt alle 10 Sekunden eine Zeile nach /var/tmp/ticker.log
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/ticker.sh
    Restart=on-failure
    RestartSec=1
    [Install]
    WantedBy=multi-user.target
  3. systemd kennt neue Dateien erst nach einem daemon-reload. Das brauchst du nach jeder Änderung an einer Unit-Datei:
    Terminal-Fenster
    sudo systemctl daemon-reload
    sudo systemctl start ticker
    systemctl status ticker
  4. Prüfen, ob er arbeitet, und dann für den Boot aktivieren:
    Terminal-Fenster
    tail -f /var/tmp/ticker.log
    sudo systemctl enable ticker
    journalctl -u ticker
    tail -f beendest du mit Ctrl+C. Im Journal stehen bisher nur «Started» und «Stopped», weil das Skript selbst nichts auf die Standardausgabe schreibt. Alles, was ein Dienst auf stdout oder stderr ausgibt, landet automatisch im Journal, darum brauchen Dienste oft gar keine eigene Logdatei.
Lösung anzeigen
Terminal-Fenster
script labs/linux/09-dienste/dienste.txt
systemctl status ticker
systemctl is-active ticker
systemctl is-enabled ticker
tail -n 5 /var/tmp/ticker.log
sudo systemctl stop ticker
systemctl status ticker
sudo systemctl start ticker
# Antwort 1: Nach dem Reboot läuft er nicht, weil kein Link in multi-user.target.wants existiert.
sudo kill -9 "$(systemctl show ticker -p MainPID --value)"
sleep 6
systemctl status ticker
# Antwort 2: Der Prozess stirbt, systemd meldet "failed" und startet ihn wegen Restart=on-failure nach einer Sekunde mit neuer PID.
exit
cp /etc/systemd/system/ticker.service /usr/local/bin/ticker.sh labs/linux/09-dienste/

Selbstcheck · 5 Fragen

1. Ein Dienst ist `enabled` und `inactive`. Was gilt?
2. Was musst du nach dem Bearbeiten einer Unit-Datei tun?
3. Was ist der Unterschied zwischen reload und restart?
4. Welche Aussagen zu ExecStart= stimmen?
5. Was legt `systemctl enable nginx` konkret an?