← Zurück

Systemd‑Dienst anlegen (Service)

system systemd services

Systemd‑Dienst anlegen (Service)

Ziel: App als systemd‑Service betreiben (autostart, logging, restart), sicher ausführen (eigener User, Sandboxing), sauber debuggen.


Inhalt


0) Voraussetzungen & Struktur

# Pakete (Beispiel für Python‑App)
sudo apt-get update -y
sudo apt-get install -y python3-venv python3-pip
# Zielstruktur (Beispiel)
/opt/myapp/               # Code
/opt/myapp/venv/          # Python‑Venv
/etc/myapp/myapp.env      # optional: Environment‑Variablen

1) Service‑User & App‑Verzeichnis

# dedizierten System‑User ohne Login anlegen
sudo useradd --system --home /opt/myapp --shell /usr/sbin/nologin --create-home appuser

# Besitzrechte übergeben
sudo chown -R appuser:appuser /opt/myapp

Tipp: Für Git‑Deploy o. ä. kannst du zunächst als normaler User arbeiten und am Ende auf appuser chownen.


2) Beispiel: Python‑App in venv

# venv erstellen (falls noch nicht vorhanden)
python3 -m venv /opt/myapp/venv

# Abhängigkeiten installieren
/opt/myapp/venv/bin/pip install --upgrade pip
/opt/myapp/venv/bin/pip install -r /opt/myapp/requirements.txt  # falls vorhanden

Optionale Env‑Datei

# .env für Configs
sudo mkdir -p /etc/myapp
sudo tee /etc/myapp/myapp.env >/dev/null <<'EOF'
ENV=production
PORT=8000
EOF
sudo chown root:root /etc/myapp/myapp.env
sudo chmod 640 /etc/myapp/myapp.env

3) Unit‑Datei erstellen (/etc/systemd/system/myapp.service)

sudo tee /etc/systemd/system/myapp.service >/dev/null <<'UNIT'
[Unit]
Description=MyApp Flask Service
After=network-online.target
Wants=network-online.target

[Service]
# Laufkonto (kein root!)
User=appuser
Group=appuser

# Arbeitsverzeichnis
WorkingDirectory=/opt/myapp

# (optional) Environment
EnvironmentFile=/etc/myapp/myapp.env

# App starten (Beispiel: Python Main)
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py

# Graceful Stop (falls benötigt)
# ExecStop=/bin/kill -s SIGINT $MAINPID
# TimeoutStopSec=20

# Neustart-Verhalten
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=3

# Security: sinnvolle Defaults (siehe Abschnitt 6)
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ReadWritePaths=/opt/myapp

# Logging (journald)
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
UNIT

Hinweis: Nach jeder Änderung an der Unit: sudo systemctl daemon-reload


4) Aktivieren, Starten, Reload

# Units neu einlesen
sudo systemctl daemon-reload

# Starten & beim Boot aktivieren
sudo systemctl enable --now myapp
# Status & Logs
systemctl status myapp --no-pager
journalctl -u myapp -n 100 --no-pager
# Änderungen übernehmen
sudo systemctl reload-or-restart myapp
# oder nur neu starten:
sudo systemctl restart myapp

5) Logging & Debugging

# Live-Logs
journalctl -u myapp -f
# Ausführliches Fehlerjournal (letzte Einträge)
journalctl -xeu myapp
# Effektive Unit inkl. Drop-ins anzeigen
systemctl cat myapp
# Validierung der Unit
systemd-analyze verify /etc/systemd/system/myapp.service
# Security-Bewertung
systemd-analyze security myapp.service
# Gescheiterte Starts zurücksetzen
sudo systemctl reset-failed myapp

6) Security‑Hardening (empfohlen)

Grundsatz: so wenig Rechte/Angriffsfläche wie möglich. Ergänzend zu den Defaults aus der Beispiel‑Unit:

# Ergänzende Direktiven (in der [Service]-Sektion)
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_NET_ADMIN
AmbientCapabilities=
ProtectClock=true
ProtectHostname=true
ProtectKernelLogs=true
MemoryDenyWriteExecute=true
LockPersonality=true
PrivateDevices=true
PrivateUsers=true
RestrictSUIDSGID=true
ProtectProc=invisible
ProcSubset=pid
RestrictNamespaces=yes
SystemCallFilter=@system-service @basic-io @file-system
SystemCallArchitectures=native
IPAddressDeny=any           # standardmäßig alles verbieten
# IPAddressAllow=127.0.0.1  # ggf. gezielt erlauben

Achtung: Einige Apps benötigen zusätzliche Capabilities oder Netzwerkzugriff – dann selektiv erlauben (IPAddressAllow=).


7) Ressourcenlimits & Autorestart

# In [Service]:
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=3

# In [Service] oder [Unit] (Ressourcen):
CPUQuota=50%          # max. CPU
MemoryMax=500M        # RAM-Grenze
TasksMax=256

8) Praktische Features (Runtime/State/Logs, Environment, Socket‑Activation)

A) Verzeichnisse automatisch anlegen (root kümmert sich):

# In [Service]:
RuntimeDirectory=myapp
StateDirectory=myapp
CacheDirectory=myapp
LogsDirectory=myapp
# → erstellt /run/myapp, /var/lib/myapp, /var/cache/myapp, /var/log/myapp (Owner=User=appuser)

B) Environment direkt in der Unit (statt Datei):

Environment="ENV=production" "PORT=8000"

C) Socket‑Activation (optional, nur wenn deine App darauf ausgelegt ist):

# /etc/systemd/system/myapp.socket
sudo tee /etc/systemd/system/myapp.socket >/dev/null <<'SOCK'
[Unit]
Description=MyApp Socket

[Socket]
ListenStream=127.0.0.1:8000
NoDelay=true

[Install]
WantedBy=sockets.target
SOCK

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.socket
# myapp.service startet dann „on demand“

9) User‑Services (systemctl --user)

Für pro‑User‑Dienste ohne root. Sitzungslos machen mit „Linger“. Beispiel für User deploy.

# Linger aktivieren (damit User‑Dienste auch ohne Login laufen)
sudo loginctl enable-linger deploy

# als User deploy:
sudo -iu deploy
systemctl --user enable --now myapp
systemctl --user status myapp

Die Unit liegt dann unter ~/.config/systemd/user/myapp.service (nicht in /etc/systemd/system).


10) Wartung, Update & Entfernen

# Code aktualisieren (Beispiel Python)
/opt/myapp/venv/bin/pip install -r /opt/myapp/requirements.txt
sudo systemctl reload-or-restart myapp
# Unit neu einlesen (nach Änderungen an der Datei)
sudo systemctl daemon-reload
# Dienst sauber entfernen
sudo systemctl disable --now myapp
sudo rm /etc/systemd/system/myapp.service
sudo systemctl daemon-reload

Kompakte Beispiel‑Unit (Copy/Paste)

[Unit]
Description=MyApp Service
After=network-online.target
Wants=network-online.target

[Service]
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ReadWritePaths=/opt/myapp
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Quick‑Commands (Spickzettel)

# Standardlauf
sudo systemctl enable --now myapp
systemctl status myapp --no-pager
journalctl -u myapp -f

# Änderungen übernehmen
sudo systemctl daemon-reload
sudo systemctl reload-or-restart myapp

# Diagnose
systemctl cat myapp
systemd-analyze verify /etc/systemd/system/myapp.service
systemd-analyze security myapp.service
journalctl -xeu myapp