Menu
Szybki wybór
Hosting Domeny VPS SSL Kalkulator Porównania FAQ
Aktywne kody
Wszystkie kody rabatowe

systemd — tworzenie i zarządzanie serwisami

Opublikowano: 9 kwietnia 2026 · Kategoria: VPS / Linux

systemd to system inicjalizacji i menedżer serwisów obecny w każdej nowoczesnej dystrybucji Linux (Ubuntu, Debian, CentOS, Fedora). Gdy wdrażasz aplikację na VPS — Node.js, Python, Go, Java — musisz upewnić się że uruchamia się automatycznie po restarcie serwera, restartuje się po awarii i ma dostęp do odpowiednich zmiennych środowiskowych. To właśnie robi systemd unit file.

Struktura pliku .service

Plik unit serwisu składa się z trzech sekcji: [Unit] (metadane i zależności), [Service] (jak uruchomić proces) i [Install] (kiedy włączyć przy starcie systemu). Pliki systemowe leżą w /etc/systemd/system/.

# Minimalna struktura pliku .service
# /etc/systemd/system/moja-aplikacja.service

[Unit]
Description=Moja aplikacja webowa
After=network.target

[Service]
ExecStart=/usr/bin/node /opt/app/index.js
Restart=on-failure

[Install]
WantedBy=multi-user.target

Sekcja [Unit] — metadane i zależności

Sekcja [Unit] definiuje opis serwisu i jego relacje z innymi jednostkami systemd:

  • Description= — czytelny opis wyświetlany przez systemctl status.
  • After=network.target — serwis startuje dopiero po zainicjalizowaniu sieci. Dla bazy danych: After=postgresql.service.
  • Requires= — twarda zależność: jeśli wymagany serwis nie wystartuje, ten też się nie uruchomi. Używaj ostrożnie.
  • Wants= — miękka zależność: systemd spróbuje uruchomić wymieniony serwis, ale niepowodzenie go nie blokuje.
  • BindsTo= — serwis jest ściśle powiązany: jeśli powiązany serwis się zatrzyma, ten też się zatrzyma.

Sekcja [Service] — typ i uruchomienie

Type= — rodzaj procesu

Type Kiedy używać Opis
simple Node.js, Python, większość aplikacji ExecStart = główny proces. systemd nie czeka na gotowość
forking Tradycyjne daemony (nginx, Apache) Aplikacja forkuje się, rodzic kończy. Wymaga PIDFile=
notify Aplikacje z biblioteką libsystemd Aplikacja wysyła sd_notify("READY=1") gdy jest gotowa
oneshot Skrypty jednorazowe, migration runner Proces się kończy — systemd czeka na zakończenie
idle Rzadko używany Uruchomienie odkładane do braku aktywnych zadań

ExecStart, ExecStop, ExecReload

ExecStart= to komenda uruchamiająca serwis — musi być pełna ścieżka bezwzględna (np. /usr/bin/node, nie samo node). Możesz podać kilka ExecStartPre= komend do wykonania przed startem (np. migracje bazy):

[Service]
# Wykonaj migracje przed startem aplikacji
ExecStartPre=/usr/bin/node /opt/app/migrate.js

# Właściwy start
ExecStart=/usr/bin/node /opt/app/server.js

# Graceful reload (np. kill -HUP dla nginx)
ExecReload=/bin/kill -HUP $MAINPID

# Graceful stop
ExecStop=/bin/kill -TERM $MAINPID

Restart policies

  • Restart=no — nigdy nie restartuj (domyślnie). Dla jednorazowych skryptów.
  • Restart=on-failure — restart tylko przy błędzie (exit code != 0, sygnał, timeout). Nie restartuje po systemctl stop.
  • Restart=on-abnormal — restart po sygnale, timeout, watchdog. Nie po exit code.
  • Restart=always — restart zawsze, nawet po ręcznym zatrzymaniu. Ostrożnie!
  • RestartSec=5 — czekaj 5 sekund przed restartem (zapobiega loopowi awarii).
  • StartLimitBurst=5 i StartLimitIntervalSec=30 — max 5 restartów w ciągu 30 sekund, potem serwis wchodzi w stan failed.

EnvironmentFile — bezpieczne sekrety

Nie wpisuj haseł i kluczy API bezpośrednio do pliku .service — plik jest widoczny przez systemctl cat dla każdego użytkownika z dostępem do systemd. Zamiast tego użyj EnvironmentFile:

[Service]
# Plik z sekretami — chmod 640, własność root:www-data
EnvironmentFile=/etc/myapp/production.env

# Można też podać zmienne bezpośrednio (tylko dla niesekretnych)
Environment=NODE_ENV=production
Environment=PORT=3000

ExecStart=/usr/bin/node /opt/app/server.js
# /etc/myapp/production.env
DATABASE_URL=postgresql://user:haslo@localhost/mydb
SECRET_KEY=klucz_tajny_xyz
REDIS_URL=redis://localhost:6379
# Zabezpiecz plik sekretów
sudo chmod 640 /etc/myapp/production.env
sudo chown root:www-data /etc/myapp/production.env

Watchdog — monitoring procesu

Watchdog to mechanizm wykrywający zawieszony proces (żywy, ale niereagujący). Aplikacja musi cyklicznie wysyłać sygnał "WATCHDOG=1" przez sd_notify(). Jeśli nie wyśle w ciągu WatchdogSec, systemd restartuje serwis:

[Service]
Type=notify
WatchdogSec=30s          # Restart jeśli brak WATCHDOG=1 przez 30s
Restart=on-failure

Kompletny przykład — serwis Node.js

# /etc/systemd/system/myapp.service

[Unit]
Description=Moja aplikacja Node.js
Documentation=https://github.com/firma/myapp
After=network.target postgresql.service redis.service
Wants=postgresql.service redis.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/app
EnvironmentFile=/etc/myapp/production.env
Environment=NODE_ENV=production
Environment=PORT=3000

# Migracja bazy przed startem (opcjonalnie)
ExecStartPre=/usr/bin/node /opt/app/scripts/migrate.js

ExecStart=/usr/bin/node /opt/app/server.js
ExecReload=/bin/kill -HUP $MAINPID

# Restart tylko przy błędach, z 5s opóźnieniem
Restart=on-failure
RestartSec=5s
StartLimitBurst=5
StartLimitIntervalSec=60s

# Limity zasobów
LimitNOFILE=65536
MemoryMax=512M

# Logowanie przez journald
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target
# Załaduj zmiany i uruchom
sudo systemctl daemon-reload
sudo systemctl enable myapp    # Autostart przy restarcie
sudo systemctl start myapp     # Uruchom teraz
sudo systemctl status myapp    # Sprawdź stan

Kompletny przykład — serwis Python (Gunicorn)

# /etc/systemd/system/mydjango.service

[Unit]
Description=Django aplikacja przez Gunicorn
After=network.target

[Service]
Type=notify
User=django
Group=django
WorkingDirectory=/opt/django-app
EnvironmentFile=/etc/django/production.env
ExecStart=/opt/django-app/venv/bin/gunicorn \
    --workers 4 \
    --bind unix:/run/gunicorn.sock \
    myproject.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
Restart=on-failure

[Install]
WantedBy=multi-user.target

Zarządzanie serwisami — systemctl

Komenda Opis
systemctl start myapp Uruchom serwis teraz
systemctl stop myapp Zatrzymaj serwis
systemctl restart myapp Zatrzymaj i uruchom ponownie
systemctl reload myapp Graceful reload (ExecReload)
systemctl enable myapp Autostart przy restarcie systemu
systemctl disable myapp Wyłącz autostart
systemctl status myapp Pokaż stan i ostatnie logi
systemctl daemon-reload Przeładuj pliki .service po zmianach
journalctl -u myapp -f Śledź logi na żywo
journalctl -u myapp -n 50 --no-pager Ostatnie 50 linii logów

Najczęstsze pytania

Jaka jest różnica między Type=simple a Type=forking w systemd? +
Type=simple (domyślny) oznacza że proces uruchomiony przez ExecStart jest głównym procesem serwisu — systemd nie czeka na nic i od razu uznaje serwis za uruchomiony. Type=forking oznacza że aplikacja startuje, forkuje się do tła i kończy proces rodzica. systemd czeka na zakończenie procesu rodzica i śledzi dziecko przez PIDFile. Type=notify to ulepszony simple — aplikacja sama wysyła do systemd powiadomienie sd_notify("READY=1") kiedy jest gotowa, co pozwala precyzyjnie określić moment gotowości.
Czym różni się Restart=on-failure od Restart=always? +
Restart=on-failure restartuje serwis tylko gdy zakończy się z niezerowym kodem wyjścia (błędem), sygnałem lub timeoutem — NIE restartuje gdy serwis zakończy się normalnie (exit code 0, np. przy ręcznym systemctl stop). Restart=always restartuje serwis w każdym przypadku — nawet po ręcznym zatrzymaniu przez systemctl stop. Dla serwerów produkcyjnych (Node.js, Python) najczęściej używa się on-failure. Dla jobów cyklicznych (oneshot) — on-failure lub no.
Jak zobaczyć logi serwisu systemd? +
Użyj journalctl -u nazwa-serwisu. Przydatne opcje: -f (follow, live tail jak tail -f), -n 100 (ostatnie 100 linii), --since "2026-01-01" (od daty), --until "2026-01-02" (do daty), -p err (tylko błędy i powyżej). Kombinacja: journalctl -u myapp -f -n 50 pokazuje ostatnie 50 linii i śledzi na żywo. Jeśli serwis nie startuje, journalctl -u myapp --no-pager | tail -30 da ostatnie komunikaty błędów.
Czym jest EnvironmentFile w systemd i kiedy go używać? +
EnvironmentFile wskazuje na plik tekstowy z parami KLUCZ=WARTOŚĆ, które są ładowane jako zmienne środowiskowe procesu. Używaj go zamiast hardcodowania sekretów (haseł, kluczy API) w pliku .service. Plik .env powinien mieć chmod 640 i być własnością roota lub specjalnego użytkownika usługi. Przykład: EnvironmentFile=/etc/myapp/production.env ładuje wszystkie zmienne z tego pliku przed uruchomieniem ExecStart. To bezpieczniejsze niż Environment=DB_PASSWORD=tajne w pliku .service.

Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.