SSD Nodes Learn
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-07-24

Programm als systemd service auf VPS starten

Lernen Sie, wie Sie Programme via systemd dauerhaft ausführen. Erstellen Sie Service-Dateien für automatischen Neustart, Timer und sichere Logs im Journal.

Was ein systemd-Service ist und warum man ihn benötigt

Ein systemd-Service ist eine kleine Textdatei, die dem Server mitteilt, wie ein Programm ausgeführt werden soll: Start beim Booten, Neustart nach einem Absturz und Weiterleitung der Ausgaben an das Systemlog. Das ist die gesamte Aufgabe. Ein Programm, das Sie manuell in einer SSH-Sitzung starten, wird beendet, sobald Sie sich ausloggen oder der Server neu startet. Ein Programm, das in einen systemd-Service eingebettet ist, läuft weiter, da der Server das Programm verwaltet und nicht Ihre Shell.

systemd ist das Init-System unter Ubuntu, Debian, Fedora und den meisten modernen Linux-Servern. Es ist der erste Prozess, der gestartet wird, und überwacht alle anderen Prozesse. Wenn Sie eine Service-Datei erstellen, übergeben Sie Ihr Programm diesem Supervisor. Diese Anleitung zeigt die kleinste funktionierende Einheit, die drei Abschnitte, die jede Einheit besitzt, die Aktivierung und das Auslesen der Logs, die zeitgesteuerte Ausführung mit einem Timer sowie die Einschränkung der Berechtigungen, damit das Programm mit minimalen Privilegien läuft.

Der kleinste funktionierende Service

Eine Service-Datei liegt in /etc/systemd/system/, endet auf .service und benötigt nur wenige Zeilen. Erstellen Sie eine Datei für ein Programm unter /usr/local/bin/myapp:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My application

[Service]
ExecStart=/usr/local/bin/myapp

[Install]
WantedBy=multi-user.target

Dies ist eine vollständige, funktionierende Einheit. ExecStart ist der Befehl zum Ausführen. WantedBy=multi-user.target bedeutet, dass der Service startet, sobald der Server den normalen Multi-User-Betrieb erreicht hat; dies bewirkt den Start beim Booten. Alles Weitere dient der Feinabstimmung.

Die drei Abschnitte und ihre Funktionen

Jede Unit-Datei ist in Abschnitte in eckigen Klammern unterteilt. Ein Service verwendet drei Abschnitte.

[Unit] beschreibt den Service und seine Abhängigkeiten. Die zwei am häufigsten verwendeten Zeilen sind:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description ist die menschlich lesbare Bezeichnung, die in systemctl status angezeigt wird. After=network-online.target weist systemd an, das Programm erst nach dem Aufbau der Netzwerkverbindung zu starten. Dies ist wichtig für alle Prozesse, die einen Port binden oder eine ausgehende Verbindung herstellen.

[Service] definiert die Ausführung des Programms. Hier werden die meisten Einstellungen vorgenommen:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

User=myapp führt das Programm unter einem unprivilegierten Benutzer anstelle von root aus. Dies ist die wichtigste Zeile für die Sicherheit. Restart=on-failure und RestartSec=5 haben eigene Abschnitte, da sie der Hauptgrund für die Erstellung eines Services sind.

[Install] definiert die Aktionen beim Aktivieren des Services:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target verknüpft den Service mit dem Bootvorgang, wenn Sie systemctl enable ausführen. Ohne einen [Install]-Abschnitt kann ein Service manuell gestartet werden, startet aber nach einem Neustart nicht automatisch.

Aktivieren und überwachen

Nach dem Schreiben oder Bearbeiten einer Unit-Datei muss systemd neu geladen werden, damit die Änderungen übernommen werden. Aktivieren und starten Sie den Dienst anschließend in einem Schritt:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload wird oft vergessen: systemd zwischenspeichert Unit-Dateien. Eine Änderung wird erst nach einem Reload wirksam. enable --now aktiviert den Dienst für den Systemstart und startet ihn sofort. Überprüfen Sie dies mit:

sudo systemctl status myapp.service
* myapp.service - My application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
   Main PID: 4123 (myapp)

Active: active (running) und enabled sind die gewünschten Ergebnisse. Um die Programmausgabe zu lesen, fragen Sie das Journal gezielt nach dieser Unit ab:

sudo journalctl -u myapp.service -f

Der -f verfolgt neue Zeilen in Echtzeit, ähnlich wie tail -f. Alle Ausgaben Ihres Programms an standard output oder standard error werden hier protokolliert, ohne dass eine eigene Logging-Konfiguration erforderlich ist.

Neustart bei Fehlern, der Grund für Ihr Interesse

Der Hauptvorteil eines Service besteht darin, dass systemd Ihr Programm bei einem Absturz neu startet. Zwei Zeilen ermöglichen dies:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure startet das Programm neu, wenn es mit einem Exit-Code ungleich Null beendet wird oder durch ein Crash-Signal wie SIGKILL oder SIGSEGV abstürzt. Ein sauberes Beenden oder ein Stoppen durch SIGTERM, SIGINT, SIGHUP oder SIGPIPE löst den Neustart nicht aus. RestartSec=5 wartet fünf Sekunden zwischen den Versuchen. Dies verhindert eine Endlosschleife, falls ein Programm sofort abstürzt. Testen Sie dies, indem Sie den Prozess beenden und beobachten, wie systemd ihn wieder startet. Verwenden Sie SIGKILL: Da der Standard-SIGTERM als sauberes Beenden gilt, würde on-failure den Service nicht neu starten:

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

Innerhalb von fünf Sekunden zeigt der Status erneut einen neuen Main PID und active (running) an. Dies ist die gesamte Funktionalität. Das ist der Grund, warum ein Service besser ist, als ein Programm in tmux oder screen laufen zu lassen.

Führen Sie es als unprivilegierten Benutzer aus und härten Sie es

Ein Dienst, der als root läuft, kann bei einer Programmausnutzung (Exploit) alles auf Ihrem Server ausführen. Lassen Sie den Dienst unter einem eigenen Benutzer laufen und nutzen Sie systemd-Direktiven, um ihn zu isolieren. Erstellen Sie zuerst einen System-Account ohne Login-Berechtigung und ohne Home-Verzeichnis:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

Setzen Sie anschließend User=myapp und fügen Sie Härtungs-Zeilen in [Service] hinzu:

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Jede Zeile entfernt eine Funktion, die das Programm nicht benötigt. NoNewPrivileges=true verhindert, dass der Prozess neue Privilegien erlangt, selbst durch ein setuid-Binary. PrivateTmp=true weist ihm einen privaten /tmp zu, den kein anderer Prozess sehen kann. ProtectSystem=strict macht das gesamte Dateisystem schreibgeschützt, außer für die Pfade, die Sie mit ReadWritePaths= angeben. ProtectHome=true verbirgt /home vollständig. Dies entspricht dem Prinzip der geringsten Privilegien (Least Privilege), ähnlich wie ein Dienst hinter einer Firewall platziert wird: Geben Sie ihm nur das, was er benötigt. Wenn Sie die Anleitung zum Schließen der IPv6-Firewall-Lücke auf einem VPS gelesen haben, ist dies die entsprechende Umsetzung direkt auf dem Host. Kombinieren Sie diese Härtung für Internet-exponierte Dienste mit Fail2ban vor SSH und einer Firewall mit einer Default-Deny-Regel.

Anstatt alles manuell zu tippen und eine Direktive falsch zu schreiben, generieren Sie eine vollständige, gehärtete Unit und kopieren Sie diese heraus:

Toolsystemd service and timer generator

Timer: der moderne cron

Ein systemd timer führt einen Service nach einem Zeitplan aus. Er ist der moderne Ersatz für einen cron job. Ein timer besteht aus zwei Dateien: einer .service, die die Arbeit ausführt, und einer .timer, die den Zeitpunkt festlegt. Angenommen, Sie möchten jeden Tag um 3 Uhr morgens ein Backup erstellen. Der service führt die Aufgabe einmal aus und beendet sich:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

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

Type=oneshot teilt systemd mit, dass das Programm ausgeführt wird, fertiggestellt wird und beendet ist, anstatt dauerhaft im Speicher zu bleiben. Der timer plant die Ausführung:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 bedeutet jeden Tag um 3 Uhr morgens. Testen Sie jeden Kalenderexpression mit systemd-analyze calendar "*-*-* 03:00:00". Dieser Befehl bestätigt das Parsen und gibt die nächsten Ausführungszeitpunkte aus. Persistent=true führt einen verpassten job sofort aus, sobald der Server wieder läuft, falls er um 3 Uhr morgens ausgeschaltet war. Dies kann cron nicht leisten. Beachten Sie, dass ein timer über timers.target aktiviert wird, nicht über multi-user.target. Aktivieren Sie den timer, nicht den service:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers zeigt jeden timer mit dem nächsten und dem letzten Ausführungszeitpunkt an. So sehen Sie auf einen Blick, wann Ihr job als Nächstes ausgeführt wird. Der obige generator erstellt die Paarung aus .service und .timer automatisch, wenn Sie den timer mode aktivieren. Im Vergleich zu einer cron zeile bietet ein timer echte logs im journal, dieselben hardening directives wie jeder service und das Nachholen verpasster läufe, das Persistent=true bereitstellt. Cron ist für einfache aufgaben weiterhin geeignet; ein timer ist das bessere Werkzeug, sobald die aufgabe kritisch wird.

FAQ

Was ist der Unterschied zwischen einem systemd-Service und einem cron job?

Ein Service hält ein Programm dauerhaft am Laufen: Er startet beim Booten, startet bei Fehlern neu und schreibt Logs in das journal. Ein cron job führt einen kurzen Befehl zu einem festgelegten Zeitpunkt aus und beendet sich danach. Wenn Sie eine Zeitplanung benötigen, aber zusätzlich journal-Logs, Hardening und das Nachholen versäumter Durchläufe wünschen, verwenden Sie einen systemd timer. Dieser kombiniert einen .timer Zeitplan mit einem oneshot Service und ersetzt cron für die meisten Server-Aufgaben.

Wo speichere ich meine systemd-Service-Datei?

Speichern Sie eigene Units in /etc/systemd/system/ mit einer Dateiendung auf .service. Dieses Verzeichnis ist für vom Administrator hinzugefügte Units gedacht und hat Vorrang vor Units aus Paketen in /lib/systemd/system/. Führen Sie nach dem Erstellen oder Bearbeiten einer Datei sudo systemctl daemon-reload aus, damit systemd die Änderung übernimmt.

Wie bewirke ich einen Neustart eines Services nach einem Absturz?

Fügen Sie Restart=on-failure und RestartSec=5 im Abschnitt [Service] hinzu, führen Sie sudo systemctl daemon-reload aus und starten Sie den Service neu. systemd startet das Programm neu, wenn es mit einem Exit-Code ungleich Null beendet wird oder durch ein Crash-Signal abstürzt. Zwischen den Versuchen wird eine Pause von fünf Sekunden eingehalten. Testen Sie dies mit sudo systemctl kill -s SIGKILL myapp.service — SIGTERM ist das Standard-Signal und gilt als sauberer Stopp, der on-failure nicht auslöst — und beobachten Sie, wie systemctl status innerhalb weniger Sekunden eine neue PID anzeigt.

Wie führe ich einen systemd-Service als Non-Root-User aus?

Erstellen Sie ein System-Konto mit sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp und fügen Sie User=myapp im Abschnitt [Service] hinzu. Fügen Sie NoNewPrivileges=true, PrivateTmp=true und ProtectSystem=strict hinzu, damit der Prozess mit minimalen Berechtigungen ausgeführt wird. Die Ausführung als unprivilegierter User ist die wichtigste Maßnahme zur Erhöhung der Sicherheit eines Services.

Warum ist mein Service nicht gestartet?

Führen Sie systemctl status myapp.service für eine Zusammenfassung und journalctl -u myapp.service für die vollständige Ausgabe aus. Die häufigsten Ursachen sind ein falscher Pfad in ExecStart, eine fehlende WorkingDirectory, ein Berechtigungsfehler, weil User= eine Datei nicht lesen kann, oder eine vergessene sudo systemctl daemon-reload nach einer Bearbeitung. Das journal zeigt die Fehlermeldung des Programms an, die das Problem meist direkt benennt.