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.
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.