Servery a DevOps

Vlastní systemd služba: jak spustit aplikaci na pozadí trvale

Napište unit soubor pro Node.js, Python nebo bot, nastavte automatický restart, proměnné prostředí, běh pod vlastním uživatelem a čtěte logy přes journalctl.

12. 10. 2026 · 4 min čtení

Spustit aplikaci přes node app.js nebo python bot.py v SSH relaci funguje do chvíle, než se odpojíte. nohup, screen nebo tmux jsou jen provizorium – po restartu serveru aplikace nenaběhne a po pádu se nikdo nepostará o restart. Správné řešení na moderním Linuxu je systemd služba.

Minimální unit soubor

Vytvořte /etc/systemd/system/mojeapp.service:

[Unit]
Description=Moje webová aplikace
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mojeapp
Group=mojeapp
WorkingDirectory=/opt/mojeapp
ExecStart=/usr/bin/node /opt/mojeapp/server.js
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Načtěte a spusťte:

sudo systemctl daemon-reload
sudo systemctl enable --now mojeapp
systemctl status mojeapp

enable zajistí start po bootu, --now službu spustí hned.

Vysvětlení klíčových voleb

Volba Význam
After=network-online.target spustit až po zprovoznění sítě
Type=simple proces běží v popředí (nedemonizuje se sám)
User= služba neběží jako root
WorkingDirectory= odkud se počítají relativní cesty
ExecStart= absolutní cesta k programu
Restart=on-failure restart při nenulovém exit kódu nebo pádu
RestartSec=5 pauza před restartem

ExecStart musí obsahovat absolutní cestu. Zjistíte ji přes which node nebo which python3. Pokud používáte nvm, je node v domovském adresáři (např. /home/jan/.nvm/versions/node/v22.11.0/bin/node) – pro produkci je jednodušší nainstalovat Node systémově.

Dedikovaný uživatel

sudo useradd --system --home /opt/mojeapp --shell /usr/sbin/nologin mojeapp
sudo chown -R mojeapp:mojeapp /opt/mojeapp

Když se aplikaci podaří zneužít, útočník má jen práva tohoto uživatele, ne roota.

Python s virtuálním prostředím

Virtualenv není potřeba „aktivovat“ – stačí volat interpreter přímo z něj:

[Service]
User=bot
WorkingDirectory=/opt/bot
ExecStart=/opt/bot/.venv/bin/python -u bot.py
Restart=always
RestartSec=10

Přepínač -u vypne bufferování výstupu, takže print() se v logu objeví okamžitě. Alternativně nastavte Environment=PYTHONUNBUFFERED=1.

Pro FastAPI s Uvicornem:

ExecStart=/opt/api/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000

Proměnné prostředí a tajné klíče

Krátké hodnoty přímo v unitu:

Environment=NODE_ENV=production
Environment=PORT=3000

Tajné údaje patří do samostatného souboru:

EnvironmentFile=/etc/mojeapp/env
sudo mkdir -p /etc/mojeapp
sudo nano /etc/mojeapp/env      # DATABASE_URL=..., API_KEY=...
sudo chmod 600 /etc/mojeapp/env

Soubor má formát KLÍČ=hodnota na řádek, bez export. Soubor čte systemd (jako root), takže chmod 600 s vlastníkem root je v pořádku.

Logy

Vše, co aplikace vypíše na stdout a stderr, ukládá journald:

journalctl -u mojeapp -f            # průběžně
journalctl -u mojeapp -n 100        # posledních 100 řádků
journalctl -u mojeapp --since "1 hour ago"
journalctl -u mojeapp -b -p err     # chyby od posledního bootu

Úprava bez přepisování: drop-in

Pokud chcete změnit unit, který přišel s balíčkem, nepřepisujte ho. Použijte:

sudo systemctl edit mojeapp

Otevře se editor pro soubor /etc/systemd/system/mojeapp.service.d/override.conf. Pozor: chcete-li přepsat ExecStart, musíte ho nejdřív vyprázdnit:

[Service]
ExecStart=
ExecStart=/usr/bin/node /opt/mojeapp/server.js --verbose

Ochrana proti smyčce restartů

Pokud aplikace padá hned po startu, systemd ji po několika pokusech přestane restartovat (výchozí limit je 5 startů za 10 sekund). Řídit to můžete v sekci [Unit]:

StartLimitIntervalSec=300
StartLimitBurst=10

Po vyřešení chyby službu odblokujete přes sudo systemctl reset-failed mojeapp.

Základní zabezpečení

systemd umí aplikaci izolovat jen pár řádky:

[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/mojeapp/data

ProtectSystem=strict připojí celý systém jen pro čtení, zápis povolíte konkrétně přes ReadWritePaths. Úroveň zabezpečení ohodnotí systemd-analyze security mojeapp.

Tahák

Akce Příkaz
Po úpravě unitu sudo systemctl daemon-reload
Restart sudo systemctl restart mojeapp
Stav systemctl status mojeapp
Vypnout autostart sudo systemctl disable mojeapp
Všechny spadlé služby systemctl --failed

Pokud vaše aplikace jen potřebuje něco spouštět pravidelně, nepotřebujete trvale běžící službu – podívejte se na systemd timery.