 Autor: [Tomasz Nowosielski](/autorzy/tomasz-nowosielski) Redaktor naczelny, analityk hostingu · Zweryfikowano Kwiecień 2026

1.  [Strona główna](/) ›
2.  [Baza wiedzy](/baza-wiedzy/) ›
3.  systemd — tworzenie i zarządzanie serwisami

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

Contabo

VPS z pełnym dostępem root — idealne środowisko do własnych serwisów systemd

Root access

[Aktywuj rabat →](/out/contabo)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/contabo)

Mikrus

Tani VPS do nauki i testowania konfiguracji systemd bez ryzyka

Dev/Test

[Aktywuj rabat →](/out/mikrus)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/mikrus)

LH.pl

Polski VPS z systemd i pełnym dostępem do zarządzania serwisami

Polski VPS

[Aktywuj rabat →](/out/lh-pl)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/lh-pl)

## Powiązane strony

-   [Nginx load balancer — konfiguracja](/baza-wiedzy/nginx-load-balancer-konfiguracja)
-   [logrotate — rotacja logów na Linux](/baza-wiedzy/logrotate-konfiguracja-linux)
-   [Supervisor — process manager dla VPS](/baza-wiedzy/supervisor-process-manager-vps)
-   [Wszystkie artykuły](/baza-wiedzy/)