Systemd‑Dienst anlegen (Service)
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
appuserchownen.
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