Přejít na obsah
Aktuální kupón:sleva 10 %Uplatnit v objednávce

Aplikace 24/7 na VPS: služba systemd (Node.js, Python, Java)

Spustíte aplikaci nebo bota na VPS jako službu systemd: běh na pozadí, automatický restart po pádu i po restartu VPS, logy v journalu a tajné údaje mimo kód.

  • pokročilý
  • ~20 min
  • Aktualizováno 1. 10. 2026

Aplikace spuštěná v terminálu skončí, jakmile zavřete SSH nebo VPS restartujete. Nastavíme ji jako službu systemd: poběží na pozadí, po pádu se sama restartuje, spustí se po startu VPS a její výpis najdete v systémovém logu.

Co budete potřebovat

  • VPS u StormHostu s Ubuntu 24.04 nebo Debianem 13
  • Připojení přes SSH jako root (návod)
  • Aplikaci, kterou umíte spustit z příkazové řádky (například Discord bota v Node.js)
  • Asi 20 minut

Proč systemd#

systemd je správce služeb, který je součástí Ubuntu i Debianu. Spouští všechny systémové služby (SSH, Nginx, databáze), takže nic dalšího neinstalujete.

ZpůsobRestart po páduStart po restartu VPSLogyPoznámka
Terminál, nohup, &NeNeJen do souboruVhodné jen na zkoušku.
screen, tmuxNeNeJen v okněPraktické pro ruční práci, ne pro provoz.
pm2AnoAno (po nastavení)VlastníDalší program navíc, jen pro Node.js.
systemdAnoAnojournalSoučást systému, funguje pro jakýkoli jazyk.

Uživatel pro aplikaci#

Aplikace nemá běžet jako root. Když ji někdo napadne, získá jen práva jejího uživatele. Vytvořte systémového uživatele s domovskou složkou v /opt:

bash
useradd --system --create-home --home-dir /opt/mujbot --shell /usr/sbin/nologin mujbot
  • --system – systémový účet pro službu (nižší číslo uživatele, bez hesla).
  • --create-home --home-dir /opt/mujbot – vytvoří složku /opt/mujbot, která patří uživateli.
  • --shell /usr/sbin/nologin – pod uživatelem se nedá přihlásit přes SSH ani terminál.
  • mujbot – jméno uživatele (a zároveň stejnojmenné skupiny). Zvolte vlastní, v návodu ho pak všude nahraďte.

Node.js LTS#

Repozitář Ubuntu 24.04 obsahuje Node.js 18 a Debian 13 Node.js 20. Obě verze už autoři Node.js nepodporují (18 od 30. 4. 2025, 20 od 30. 4. 2026). Nainstalujte aktuální LTS verzi z oficiálních balíčků na nodejs.org (otevře se v novém okně). V říjnu 2026 je aktivní LTS Node.js 24 (koncem října 2026 se LTS stává Node.js 26).

Nejdřív archiv stáhněte a ověřte:

bash
apt -y install curl xz-utils
cd /tmp
NODE_SOUBOR=$(curl -fsSL https://nodejs.org/dist/latest-v24.x/SHASUMS256.txt | grep -o 'node-v[0-9.]*-linux-x64.tar.xz' | head -n 1)
curl -fsSLO "https://nodejs.org/dist/latest-v24.x/$NODE_SOUBOR"
curl -fsSL https://nodejs.org/dist/latest-v24.x/SHASUMS256.txt | grep " $NODE_SOUBOR\$" | sha256sum -c -
Výstuptext
node-v24.21.0-linux-x64.tar.xz: OK

Pokračujte, jen když vidíte OK. Při FAILED soubor smažte (rm /tmp/node-v*.tar.xz) a stažení zopakujte. Pak Node.js nainstalujte (ve stejném okně terminálu, proměnná NODE_SOUBOR musí zůstat nastavená):

bash
mkdir -p /opt/nodejs
tar -xJf "$NODE_SOUBOR" -C /opt/nodejs --strip-components=1
ln -sf /opt/nodejs/bin/node /usr/local/bin/node
ln -sf /opt/nodejs/bin/npm /usr/local/bin/npm
ln -sf /opt/nodejs/bin/npx /usr/local/bin/npx
PříkazCo dělá
apt -y install curl xz-utilsStahovací program a rozbalování formátu .tar.xz.
NODE_SOUBOR=$(… SHASUMS256.txt | grep -o … | head -n 1)Ze seznamu kontrolních součtů nejnovějšího vydání řady 24 zjistí název souboru pro Linux x64, například node-v24.21.0-linux-x64.tar.xz.
curl -fsSLO "…/$NODE_SOUBOR"Stáhne archiv; -O ho uloží pod původním názvem.
… | grep " $NODE_SOUBOR\$" | sha256sum -c -Ověří kontrolní součet staženého souboru proti oficiálnímu seznamu. Chrání před poškozeným nebo podvrženým souborem.
mkdir -p /opt/nodejsSložka pro Node.js.
tar -xJf … -C /opt/nodejs --strip-components=1Rozbalí archiv (J = formát xz) do /opt/nodejs bez nejvyšší složky s číslem verze.
ln -sf /opt/nodejs/bin/… /usr/local/bin/…Vytvoří odkazy na node, npm a npx ve složce, kde je systém hledá. Cesta /usr/local/bin/node zůstane stejná i po aktualizaci.

Ověření: node -v vypíše v24.…, npm -v číslo verze npm.

Pro jinou řadu nahraďte ve všech adresách latest-v24.x například latest-v26.x. Aktualizace Node.js = zastavit službu, smazat /opt/nodejs a postup zopakovat.

Proč ne nvm

nvm (nabízí ho stránka stažení na nodejs.org) instaluje Node.js do domovské složky uživatele do cesty s číslem verze. Pro službu systemd je potřeba pevná absolutní cesta k programu, která se po aktualizaci nezmění. Proto tu používáme oficiální archiv s odkazem v /usr/local/bin.

Aplikace#

  1. Nahrajte kód aplikace do /opt/mujbot/app (přes SFTP, návod, nebo git clone). Soubor .env s tokenem a složku node_modules nenahrávejte.

  2. Předejte soubory uživateli aplikace:

    bash
    chown -R mujbot:mujbot /opt/mujbot
  3. Nainstalujte balíčky pod uživatelem aplikace:

    bash
    cd /opt/mujbot/app
    runuser -u mujbot -- npm ci --omit=dev
    • runuser -u mujbot -- – spustí příkaz pod uživatelem mujbot (funguje i bez balíčku sudo).
    • npm ci – nainstaluje přesně verze ze souboru package-lock.json. Pokud ho projekt nemá, použijte npm install --omit=dev.
    • --omit=dev – vynechá balíčky potřebné jen pro vývoj.
  4. Vyzkoušejte aplikaci ručně: runuser -u mujbot -- node index.js. Když se spustí bez chyby, ukončete ji Ctrl+C. (Pokud potřebuje token, ruční test udělejte až po kapitole Tajné údaje.)

Tajné údaje v EnvironmentFile#

Token a hesla nepatří do kódu ani do unit souboru. Uložte je do souboru proměnných, který čte jen root, a systemd je předá aplikaci při startu.

  1. Vytvořte soubor nano /etc/mujbot.env:

    /etc/mujbot.envini
    DISCORD_TOKEN=<TOKEN_BOTA>
    NODE_ENV=production
    • Každý řádek je NAZEV=hodnota, bez export a bez mezer kolem =.
    • Řádky začínající # jsou komentáře.
    • V aplikaci hodnotu přečtete jako process.env.DISCORD_TOKEN.
  2. Omezte práva:

    bash
    chown root:root /etc/mujbot.env
    chmod 600 /etc/mujbot.env
    • chmod 600 – soubor smí číst a měnit jen root. systemd ho čte ještě před přepnutím na uživatele aplikace, proměnné se tak k aplikaci dostanou, ale soubor nikdo jiný nepřečte.

Unit soubor služby#

Vytvořte soubor nano /etc/systemd/system/mujbot.service:

/etc/systemd/system/mujbot.serviceini
[Unit]
Description=Můj Discord bot
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mujbot
Group=mujbot
WorkingDirectory=/opt/mujbot/app
EnvironmentFile=/etc/mujbot.env
ExecStart=/usr/local/bin/node /opt/mujbot/app/index.js
Restart=on-failure
RestartSec=5
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
DirektivaCo děláDoporučená hodnotaKdy změnit
Description=Popis služby ve výpisech.Název aplikace–
After=network-online.targetSlužba startuje až po nastavení sítě.network-online.target–
Wants=network-online.targetPožádá systém, ať na síť počká (After samo nestačí).network-online.target–
Type=simpleProces z ExecStart je hlavní proces služby a běží v popředí.simpleJen když aplikace sama přechází na pozadí (forking).
User=, Group=Pod jakým uživatelem aplikace běží.uživatel aplikaceNikdy ne root.
WorkingDirectory=Pracovní složka; relativní cesty v aplikaci se počítají od ní.složka aplikacePři přesunu aplikace.
EnvironmentFile=Soubor s proměnnými prostředí. Když neexistuje, služba nenastartuje (pomlčka před cestou, -/etc/…, ho udělá nepovinným)./etc/mujbot.env–
ExecStart=Příkaz pro spuštění. Program musí být zadaný absolutní cestou./usr/local/bin/node /opt/…/index.jsPodle hlavního souboru aplikace.
Restart=on-failureRestart, když proces skončí nenulovým kódem nebo ho ukončí signál (kromě SIGTERM, SIGINT, SIGHUP a SIGPIPE, které systemd bere jako řádné ukončení). Ukončení s kódem 0 restart nespustí.on-failurealways, pokud se má spustit znovu i po řádném ukončení.
RestartSec=5Pauza 5 sekund před restartem (výchozí je 100 ms).5Delší, pokud aplikace při pádu zatěžuje cizí API.
NoNewPrivileges=trueAplikace ani její podprocesy nezískají vyšší práva (například přes programy s bitem setuid).true–
WantedBy=multi-user.targetPo enable se služba spustí při startu VPS.multi-user.target–

Limit opakovaných startů

systemd ve výchozím stavu nepovolí víc než 5 startů služby za 10 sekund. S RestartSec=5 se tento limit nevyčerpá a padající služba se bude zkoušet spouštět pořád dokola každých 5 sekund. Pokud chcete po sérii pádů skončit, přidejte do části [Unit] například StartLimitIntervalSec=300 a StartLimitBurst=10 (nejvýš 10 startů za 5 minut).

Spuštění služby#

bash
systemctl daemon-reload
systemctl enable --now mujbot
systemctl status mujbot
  • systemctl daemon-reload – načte nové a změněné unit soubory. Spouštějte po každé úpravě souboru .service.
  • systemctl enable --now mujbot – zapne spuštění po startu VPS a službu hned spustí.
  • systemctl status mujbot – stav služby a posledních pár řádků logu.

Ve výpisu status sledujte:

Výstuptext
● mujbot.service - Můj Discord bot
     Loaded: loaded (/etc/systemd/system/mujbot.service; enabled; preset: enabled)
     Active: active (running) since …
   Main PID: 12345 (node)
  • Loaded: … enabled – služba se spustí po restartu VPS.
  • Active: active (running) – běží. failed znamená, že spadla; activating (auto-restart), že čeká na restart.
  • Main PID – číslo procesu aplikace.

Další příkazy: systemctl restart mujbot (restart), systemctl stop mujbot (zastavení), systemctl disable mujbot (vypnutí spouštění po startu).

Logy#

Vše, co aplikace vypíše (console.log, print, chyby), ukládá systemd do journalu.

PříkazCo ukáže
journalctl -u mujbot -fPrůběžný výpis jako živá konzole; ukončíte Ctrl+C.
journalctl -u mujbot -n 100Posledních 100 řádků.
journalctl -u mujbot --since "1 hour ago"Výpis za poslední hodinu.
journalctl -u mujbot -bVýpis od posledního startu VPS.
journalctl --disk-usageKolik místa journal zabírá.

Journal se promazává sám: ve výchozím stavu zabere nejvýš 10 % velikosti souborového systému, maximálně 4 GB.

Python a Java#

Postup je stejný, liší se jen instalace prostředí a řádek ExecStart.

Python

Python 3 je v systému. Pro aplikaci vytvořte virtuální prostředí, ať se balíčky nemíchají se systémovými:

bash
apt -y install python3-venv
runuser -u mujbot -- python3 -m venv /opt/mujbot/venv
runuser -u mujbot -- /opt/mujbot/venv/bin/pip install -r /opt/mujbot/app/requirements.txt
  • python3 -m venv /opt/mujbot/venv – vytvoří samostatné prostředí Pythonu ve složce venv.
  • /opt/mujbot/venv/bin/pip install -r … – nainstaluje balíčky ze souboru requirements.txt do tohoto prostředí.

V unit souboru použijte Python z prostředí a vypněte bufferování výstupu (jinak se logy v journalu objevují se zpožděním):

ini
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/mujbot/venv/bin/python /opt/mujbot/app/bot.py

Java

Nainstalujte běhové prostředí Javy ve verzi, kterou aplikace vyžaduje. Pro Javu 21:

bash
apt -y install openjdk-21-jre-headless

Pro Javu 25 nainstalujte místo toho balíček openjdk-25-jre-headless. Oba jsou v repozitáři Ubuntu 24.04 i Debianu 13. Verzi ověříte příkazem java -version.

V unit souboru:

ini
ExecStart=/usr/bin/java -Xms512M -Xmx1G -jar /opt/mujbot/app/aplikace.jar
SuccessExitStatus=143
  • -Xms512M -Xmx1G – počáteční a maximální velikost paměti Javy. Maximum nastavte s rezervou pod volnou RAM VPS.
  • SuccessExitStatus=143 – Java při řádném zastavení (signál SIGTERM) končí kódem 143. Bez tohoto řádku by systemd po každém systemctl stop hlásil službu jako failed.

Aktualizace aplikace#

bash
systemctl stop mujbot
cd /opt/mujbot/app
runuser -u mujbot -- git pull
runuser -u mujbot -- npm ci --omit=dev
systemctl start mujbot
  • git pull – stáhne novou verzi kódu (pokud aplikaci máte v Gitu; jinak nahrajte soubory přes SFTP a spusťte chown -R mujbot:mujbot /opt/mujbot).
  • npm ci --omit=dev – doinstaluje změněné balíčky.

Pokud se změnily jen soubory kódu, stačí systemctl restart mujbot.

Ověření#

  • systemctl is-enabled mujbot vypíše enabled, systemctl is-active mujbot vypíše active.
  • Po restartu VPS (reboot) aplikace běží bez vašeho zásahu.
  • Zkouška automatického restartu: systemctl kill -s SIGKILL mujbot (násilně ukončí proces, jako by spadl). Za 5 sekund systemctl status mujbot znovu ukáže active (running) s jiným Main PID. Obyčejné systemctl kill bez -s SIGKILL posílá SIGTERM, který systemd bere jako řádné ukončení, a s Restart=on-failure se služba nespustí znovu.

Když něco nefunguje#

status=203/EXEC#

Systemd nedokázal spustit program z ExecStart. Cesta k programu neexistuje, není absolutní, nebo soubor nejde spustit. Ověřte cestu (ls -l /usr/local/bin/node) a u skriptů první řádek (#!/usr/bin/env …) a právo spuštění.

status=217/USER#

Uživatel z User= neexistuje. Zkontrolujte jméno (id mujbot), nebo uživatele vytvořte podle kapitoly Uživatel pro aplikaci.

status=200/CHDIR#

Složka z WorkingDirectory= neexistuje nebo do ní uživatel nesmí. Zkontrolujte cestu a vlastníka (ls -ld /opt/mujbot/app).

Failed to load environment files: No such file or directory#

Soubor z EnvironmentFile= neexistuje. Zkontrolujte cestu, nebo před ni přidejte -, pokud je soubor nepovinný.

Start request repeated too quickly. (start-limit-hit)#

Služba padala příliš rychle za sebou a systemd ji přestal spouštět. Příčinu najdete v logu (journalctl -u mujbot -n 50). Po opravě vynulujte počítadlo a spusťte službu:

bash
systemctl reset-failed mujbot
systemctl start mujbot

Warning: The unit file … changed on disk. Run 'systemctl daemon-reload' to reload units.#

Upravili jste unit soubor a neprovedli systemctl daemon-reload. Spusťte ho a službu restartujte.

Aplikace hlásí EACCES nebo permission denied#

Uživatel aplikace nesmí číst nebo zapisovat soubory. Typicky po nahrání souborů jako root. Opravte vlastníka: chown -R mujbot:mujbot /opt/mujbot. Port pod 1024 (například 80) běžný uživatel otevřít nesmí; nechte aplikaci poslouchat na vyšším portu a před ni dejte Nginx (návod).

Aplikace nevidí proměnnou z .env#

Systemd soubor .env v adresáři aplikace sám nenačítá. Proměnné dejte do souboru z EnvironmentFile=, nebo je v aplikaci načtěte knihovnou (například dotenv). Po změně souboru proměnných službu restartujte.

Shrnutí

  • Aplikace běží pod vlastním uživatelem, ne jako root.
  • Unit soubor v /etc/systemd/system/ určuje uživatele, složku, příkaz se absolutní cestou a restart po pádu.
  • Tajné údaje jsou v souboru z EnvironmentFile= s právy 600.
  • Po úpravě unit souboru: systemctl daemon-reload; zapnutí a start: systemctl enable --now.
  • Logy: journalctl -u <NAZEV_SLUZBY> -f.

Hledání v návodech