Aplikácia spustená v termináli skončí, hneď ako zatvoríte SSH alebo VPS reštartujete. Nastavíme ju ako službu systemd: pobeží na pozadí, po páde sa sama reštartuje, spustí sa po štarte VPS a jej výpis nájdete v systémovom logu.
Čo budete potrebovať
- VPS u StormHostu s Ubuntu 24.04 alebo Debianom 13
- Pripojenie cez SSH ako root (návod)
- Aplikáciu, ktorú viete spustiť z príkazového riadka (napríklad Discord bota v Node.js)
- Približne 20 minút
Prečo systemd#
systemd je správca služieb, ktorý je súčasťou Ubuntu aj Debianu. Spúšťa všetky systémové služby (SSH, Nginx, databázy), takže nič ďalšie neinštalujete.
| Spôsob | Reštart po páde | Štart po reštarte VPS | Logy | Poznámka |
|---|---|---|---|---|
Terminál, nohup, & | Nie | Nie | Iba do súboru | Vhodné iba na skúšku. |
screen, tmux | Nie | Nie | Iba v okne | Praktické na ručnú prácu, nie na prevádzku. |
| pm2 | Áno | Áno (po nastavení) | Vlastné | Ďalší program navyše, iba pre Node.js. |
| systemd | Áno | Áno | journal | Súčasť systému, funguje pre akýkoľvek jazyk. |
Používateľ pre aplikáciu#
Aplikácia nemá bežať ako root. Keď ju niekto napadne, získa iba práva jej používateľa. Vytvorte systémového používateľa s domovským priečinkom v /opt:
useradd --system --create-home --home-dir /opt/mujbot --shell /usr/sbin/nologin mujbot--system– systémový účet pre službu (nižšie číslo používateľa, bez hesla).--create-home --home-dir /opt/mujbot– vytvorí priečinok/opt/mujbot, ktorý patrí používateľovi.--shell /usr/sbin/nologin– pod používateľom sa nedá prihlásiť cez SSH ani terminál.mujbot– meno používateľa (a zároveň rovnomennej skupiny). Zvoľte vlastné, v návode ho potom všade nahraďte.
Node.js LTS#
Repozitár Ubuntu 24.04 obsahuje Node.js 18 a Debian 13 Node.js 20. Obe verzie už autori Node.js nepodporujú (18 od 30. 4. 2025, 20 od 30. 4. 2026). Nainštalujte aktuálnu LTS verziu z oficiálnych balíkov na nodejs.org (otvorí sa v novom okne). V októbri 2026 je aktívna LTS Node.js 24 (koncom októbra 2026 sa LTS stáva Node.js 26).
Najprv archív stiahnite a overte:
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 -node-v24.21.0-linux-x64.tar.xz: OKPokračujte, iba keď vidíte OK. Pri FAILED súbor zmažte (rm /tmp/node-v*.tar.xz) a stiahnutie zopakujte. Potom Node.js nainštalujte (v rovnakom okne terminálu, premenná NODE_SOUBOR musí zostať nastavená):
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| Príkaz | Čo robí |
|---|---|
apt -y install curl xz-utils | Sťahovací program a rozbaľovanie formátu .tar.xz. |
NODE_SOUBOR=$(… SHASUMS256.txt | grep -o … | head -n 1) | Zo zoznamu kontrolných súčtov najnovšieho vydania radu 24 zistí názov súboru pre Linux x64, napríklad node-v24.. |
curl -fsSLO "…/$NODE_SOUBOR" | Stiahne archív; -O ho uloží pod pôvodným názvom. |
… | grep " $NODE_SOUBOR\$" | sha256sum -c - | Overí kontrolný súčet stiahnutého súboru oproti oficiálnemu zoznamu. Chráni pred poškodeným alebo podvrhnutým súborom. |
mkdir -p /opt/nodejs | Priečinok pre Node.js. |
tar -xJf … -C /opt/nodejs --strip-components=1 | Rozbalí archív (J = formát xz) do /opt/nodejs bez najvyššieho priečinka s číslom verzie. |
ln -sf /opt/nodejs/bin/… /usr/local/bin/… | Vytvorí odkazy na node, npm a npx v priečinku, kde ich systém hľadá. Cesta /usr/local/bin/node zostane rovnaká aj po aktualizácii. |
Overenie: node -v vypíše v24.…, npm -v číslo verzie npm.
Pre iný rad nahraďte vo všetkých adresách latest-v24.x napríklad latest-v26.x. Aktualizácia Node.js = zastaviť službu, zmazať /opt/nodejs a postup zopakovať.
Prečo nie nvm
nvm (ponúka ho stránka sťahovania na nodejs.org) inštaluje Node.js do domovského priečinka používateľa do cesty s číslom verzie. Pre službu systemd je potrebná pevná absolútna cesta k programu, ktorá sa po aktualizácii nezmení. Preto tu používame oficiálny archív s odkazom v /usr/local/bin.
Aplikácia#
Nahrajte kód aplikácie do
/opt/mujbot/app(cez SFTP, návod, alebogit clone). Súbor.envs tokenom a priečinoknode_modulesnenahrávajte.Odovzdajte súbory používateľovi aplikácie:
bash chown -R mujbot:mujbot /opt/mujbotNainštalujte balíčky pod používateľom aplikácie:
bash cd /opt/mujbot/app runuser -u mujbot -- npm ci --omit=devrunuser -u mujbot --– spustí príkaz pod používateľommujbot(funguje aj bez balíka sudo).npm ci– nainštaluje presne verzie zo súborupackage-lock.json. Ak ho projekt nemá, použitenpm install --omit=dev.--omit=dev– vynechá balíčky potrebné iba na vývoj.
Vyskúšajte aplikáciu ručne:
runuser -u mujbot -- node index.js. Keď sa spustí bez chyby, ukončite ju Ctrl+C. (Ak potrebuje token, ručný test urobte až po kapitole Tajné údaje.)
Tajné údaje v EnvironmentFile#
Token a heslá nepatria do kódu ani do unit súboru. Uložte ich do súboru premenných, ktorý číta iba root, a systemd ich odovzdá aplikácii pri štarte.
Vytvorte súbor
nano /etc/mujbot.env:/etc/ mujbot.env iniDISCORD_TOKEN=<TOKEN_BOTA> NODE_ENV=production- Každý riadok je
NAZEV=hodnota, bezexporta bez medzier okolo=. - Riadky začínajúce
#sú komentáre. - V aplikácii hodnotu prečítate ako
process..env. DISCORD_ TOKEN
- Každý riadok je
Obmedzte práva:
bash chown root:root /etc/mujbot.env chmod 600 /etc/mujbot.envchmod 600– súbor smie čítať a meniť iba root. systemd ho číta ešte pred prepnutím na používateľa aplikácie, premenné sa tak k aplikácii dostanú, ale súbor nikto iný neprečíta.
Unit súbor služby#
Vytvorte súbor nano /etc/:
[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| Direktíva | Čo robí | Odporúčaná hodnota | Kedy zmeniť |
|---|---|---|---|
Description= | Opis služby vo výpisoch. | Názov aplikácie | – |
After= | Služba štartuje až po nastavení siete. | network-online.target | – |
Wants= | Požiada systém, aby na sieť počkal (After samo nestačí). | network-online.target | – |
Type=simple | Proces z ExecStart je hlavný proces služby a beží v popredí. | simple | Iba keď aplikácia sama prechádza na pozadie (forking). |
User=, Group= | Pod akým používateľom aplikácia beží. | používateľ aplikácie | Nikdy nie root. |
WorkingDirectory= | Pracovný priečinok; relatívne cesty v aplikácii sa počítajú od neho. | priečinok aplikácie | Pri presune aplikácie. |
EnvironmentFile= | Súbor s premennými prostredia. Keď neexistuje, služba nenaštartuje (pomlčka pred cestou, -/etc/…, ho urobí nepovinným). | /etc/mujbot.env | – |
ExecStart= | Príkaz na spustenie. Program musí byť zadaný absolútnou cestou. | /usr/local/bin/node /opt/…/index.js | Podľa hlavného súboru aplikácie. |
Restart=on-failure | Reštart, keď proces skončí nenulovým kódom alebo ho ukončí signál (okrem SIGTERM, SIGINT, SIGHUP a SIGPIPE, ktoré systemd berie ako riadne ukončenie). Ukončenie s kódom 0 reštart nespustí. | on-failure | always, ak sa má spustiť znova aj po riadnom ukončení. |
RestartSec=5 | Pauza 5 sekúnd pred reštartom (predvolené je 100 ms). | 5 | Dlhšia, ak aplikácia pri páde zaťažuje cudzie API. |
NoNewPrivileges=true | Aplikácia ani jej podprocesy nezískajú vyššie práva (napríklad cez programy s bitom setuid). | true | – |
WantedBy= | Po enable sa služba spustí pri štarte VPS. | multi-user.target | – |
Limit opakovaných štartov
systemd v predvolenom stave nepovolí viac než 5 štartov služby za 10 sekúnd. S RestartSec=5 sa tento limit nevyčerpá a padajúca služba sa bude skúšať spúšťať stále dokola každých 5 sekúnd. Ak chcete po sérii pádov skončiť, pridajte do časti [Unit] napríklad StartLimitIntervalSec= a StartLimitBurst=10 (najviac 10 štartov za 5 minút).
Spustenie služby#
systemctl daemon-reload
systemctl enable --now mujbot
systemctl status mujbotsystemctl daemon-reload– načíta nové a zmenené unit súbory. Spúšťajte po každej úprave súboru.service.systemctl enable --now mujbot– zapne spustenie po štarte VPS a službu hneď spustí.systemctl status mujbot– stav služby a posledných pár riadkov logu.
Vo výpise status sledujte:
● 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 sa spustí po reštarte VPS.Active: active (running)– beží.failedznamená, že spadla;activating (auto-restart), že čaká na reštart.Main PID– číslo procesu aplikácie.
Ďalšie príkazy: systemctl restart mujbot (reštart), systemctl stop mujbot (zastavenie), systemctl disable mujbot (vypnutie spúšťania po štarte).
Logy#
Všetko, čo aplikácia vypíše (console.log, print, chyby), ukladá systemd do journalu.
| Príkaz | Čo ukáže |
|---|---|
journalctl -u mujbot -f | Priebežný výpis ako živá konzola; ukončíte Ctrl+C. |
journalctl -u mujbot -n 100 | Posledných 100 riadkov. |
journalctl -u mujbot --since "1 hour ago" | Výpis za poslednú hodinu. |
journalctl -u mujbot -b | Výpis od posledného štartu VPS. |
journalctl --disk-usage | Koľko miesta journal zaberá. |
Journal sa premazáva sám: v predvolenom stave zaberie najviac 10 % veľkosti súborového systému, maximálne 4 GB.
Python a Java#
Postup je rovnaký, líši sa iba inštalácia prostredia a riadok ExecStart.
Python
Python 3 je v systéme. Pre aplikáciu vytvorte virtuálne prostredie, aby sa balíčky nemiešali so systémovými:
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.txtpython3 -m venv /opt/mujbot/venv– vytvorí samostatné prostredie Pythonu v priečinkuvenv./opt/mujbot/venv/bin/pip install -r …– nainštaluje balíčky zo súborurequirements.txtdo tohto prostredia.
V unit súbore použite Python z prostredia a vypnite bufferovanie výstupu (inak sa logy v journale objavujú s oneskorením):
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/mujbot/venv/bin/python /opt/mujbot/app/bot.pyJava
Nainštalujte behové prostredie Javy vo verzii, ktorú aplikácia vyžaduje. Pre Javu 21:
apt -y install openjdk-21-jre-headlessPre Javu 25 nainštalujte namiesto toho balík openjdk-25-jre-headless. Oba sú v repozitári Ubuntu 24.04 aj Debianu 13. Verziu overíte príkazom java -version.
V unit súbore:
ExecStart=/usr/bin/java -Xms512M -Xmx1G -jar /opt/mujbot/app/aplikace.jar
SuccessExitStatus=143-Xms512M -Xmx1G– počiatočná a maximálna veľkosť pamäte Javy. Maximum nastavte s rezervou pod voľnú RAM VPS.SuccessExitStatus=143– Java pri riadnom zastavení (signál SIGTERM) končí kódom 143. Bez tohto riadka by systemd po každomsystemctl stophlásil službu akofailed.
Aktualizácia aplikácie#
systemctl stop mujbot
cd /opt/mujbot/app
runuser -u mujbot -- git pull
runuser -u mujbot -- npm ci --omit=dev
systemctl start mujbotgit pull– stiahne novú verziu kódu (ak aplikáciu máte v Gite; inak nahrajte súbory cez SFTP a spustitechown -R mujbot:mujbot /opt/mujbot).npm ci --omit=dev– doinštaluje zmenené balíčky.
Ak sa zmenili iba súbory kódu, stačí systemctl restart mujbot.
Overenie#
systemctl is-enabled mujbotvypíšeenabled,systemctl is-active mujbotvypíšeactive.- Po reštarte VPS (
reboot) aplikácia beží bez vášho zásahu. - Skúška automatického reštartu:
systemctl kill -s SIGKILL mujbot(násilne ukončí proces, ako keby spadol). O 5 sekúndsystemctl status mujbotznova ukážeactive (running)s inýmMain PID. Obyčajnésystemctl killbez-s SIGKILLposiela SIGTERM, ktorý systemd berie ako riadne ukončenie, a sRestart=on-failuresa služba nespustí znova.
Keď niečo nefunguje#
status=203/EXEC#
Systemd nedokázal spustiť program z ExecStart. Cesta k programu neexistuje, nie je absolútna, alebo súbor sa nedá spustiť. Overte cestu (ls -l /usr/local/bin/node) a pri skriptoch prvý riadok (#!/usr/bin/env …) a právo spustenia.
status=217/USER#
Používateľ z User= neexistuje. Skontrolujte meno (id mujbot), alebo používateľa vytvorte podľa kapitoly Používateľ pre aplikáciu.
status=200/CHDIR#
Priečinok z WorkingDirectory= neexistuje alebo doň používateľ nesmie. Skontrolujte cestu a vlastníka (ls -ld /opt/mujbot/app).
Failed to load environment files: No such file or directory#
Súbor z EnvironmentFile= neexistuje. Skontrolujte cestu, alebo pred ňu pridajte -, ak je súbor nepovinný.
Start request repeated too quickly. (start-limit-hit)#
Služba padala príliš rýchlo za sebou a systemd ju prestal spúšťať. Príčinu nájdete v logu (journalctl -u mujbot -n 50). Po oprave vynulujte počítadlo a spustite službu:
systemctl reset-failed mujbot
systemctl start mujbotWarning: The unit file … changed on disk. Run 'systemctl daemon-reload' to reload units.#
Upravili ste unit súbor a nevykonali systemctl daemon-reload. Spustite ho a službu reštartujte.
Aplikácia hlási EACCES alebo permission denied#
Používateľ aplikácie nesmie čítať alebo zapisovať súbory. Typicky po nahraní súborov ako root. Opravte vlastníka: chown -R mujbot:mujbot /opt/mujbot. Port pod 1024 (napríklad 80) bežný používateľ otvoriť nesmie; nechajte aplikáciu počúvať na vyššom porte a pred ňu dajte Nginx (návod).
Aplikácia nevidí premennú z .env#
Systemd súbor .env v adresári aplikácie sám nenačítava. Premenné dajte do súboru z EnvironmentFile=, alebo ich v aplikácii načítajte knižnicou (napríklad dotenv). Po zmene súboru premenných službu reštartujte.
Zhrnutie
- Aplikácia beží pod vlastným používateľom, nie ako root.
- Unit súbor v
/etc/systemd/system/určuje používateľa, priečinok, príkaz s absolútnou cestou a reštart po páde. - Tajné údaje sú v súbore z
EnvironmentFile=s právami600. - Po úprave unit súboru:
systemctl daemon-reload; zapnutie a štart:systemctl enable --now. - Logy:
journalctl -u <NAZEV_SLUZBY> -f.