SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-28

Jak uruchomić program jako usługa systemd na VPS

Dowiedz się, jak stworzyć plik jednostki .service dla własnego programu. Zapewnij automatyczny start po boot, restart po awarii oraz logowanie błędów w journalctl systemu.

Czym jest usługa systemd i dlaczego warto jej używać

Usługa systemd to niewielki plik tekstowy, który instruuje serwer, w jaki sposób uruchomić program: ma go włączyć podczas startu systemu, zrestartować w razie awarii oraz przekierować jego wyjście do dziennika systemowego. To całe zadanie. Program uruchomiony ręcznie w sesji SSH kończy działanie w momencie wylogowania lub restartu serwera. Program opakowany w usługę systemd działa w tle, ponieważ jego właścicielem jest serwer, a nie powłoka użytkownika.

systemd to system inicjacji w Ubuntu, Debian, Fedora oraz większości nowoczesnych serwerów Linux. Jest to pierwszy proces, który startuje i nadzoruje wszystkie pozostałe. Nie zawsze tak było, a historia zastąpienia skryptów init przez systemd jest warta przeczytania, gdy zrozumiesz już działanie pliku jednostki. Tworząc plik usługi, przekazujesz swój program pod nadzór tego systemu. Ten przewodnik przedstawia najmniejszą działającą jednostkę, trzy sekcje wymagane w każdej jednostce, sposób jej aktywacji i odczytywania dzienników, uruchamianie zadań według harmonogramu za pomocą timera oraz ograniczanie uprawnień w celu zapewnienia maksymalnego bezpieczeństwa.

Najmniejsza działająca usługa

Plik usługi znajduje się w /etc/systemd/system/, kończy się rozszerzeniem .service i wymaga tylko kilku linii. Utwórz go dla programu znajdującego się w /usr/local/bin/myapp:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My application

[Service]
ExecStart=/usr/local/bin/myapp

[Install]
WantedBy=multi-user.target

To kompletna, działająca jednostka. ExecStart to polecenie, które ma zostać uruchomione. WantedBy=multi-user.target oznacza uruchomienie usługi po osiągnięciu przez serwer normalnego trybu pracy wieloużytkownikowej, co zapewnia jej start podczas rozruchu systemu. Wszystkie pozostałe elementy to jedynie doprecyzowanie konfiguracji.

Trzy sekcje i ich przeznaczenie

Każdy plik jednostki jest podzielony na sekcje ujęte w nawiasy kwadratowe. Usługa wykorzystuje trzy z nich.

[Unit] opisuje usługę oraz jej zależności. Dwie linie, które będą używane najczęściej:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description to czytelna dla człowieka etykieta widoczna w systemctl status. After=network-online.target informuje systemd, aby nie uruchamiał programu przed nawiązaniem połączenia sieciowego, co jest istotne dla każdego procesu nasłuchującego na porcie lub nawiązującego połączenia wychodzące.

[Service] określa sposób uruchomienia programu. Tutaj umieszcza się większość ustawień:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

User=myapp uruchamia program jako użytkownik bez uprawnień zamiast jako root, co stanowi najważniejszą linię z punktu widzenia bezpieczeństwa. Brak tutaj linii Type=, więc systemd przyjmuje domyślnie simple i zakłada, że proces ExecStart pozostaje na pierwszym planie; program, który przechodzi w tło (fork), wymaga odpowiedniego typu Type= dla sposobu uruchomienia, w przeciwnym razie jednostka zgłosi stan aktywny, podczas gdy właściwy demon już nie istnieje. Restart=on-failure oraz RestartSec=5 otrzymały własną sekcję poniżej, ponieważ to one są głównym powodem tworzenia własnych usług.

[Install] określa, co dzieje się podczas włączania usługi:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target to element, który łączy usługę z procesem startu systemu po wykonaniu systemctl enable. Bez sekcji [Install] usługę można uruchomić ręcznie, ale nie wystartuje ona automatycznie po restarcie systemu.

Uruchomienie i monitorowanie

Po utworzeniu lub edycji pliku jednostki należy przeładować systemd, aby wczytał zmiany, a następnie włączyć i uruchomić usługę w jednym kroku:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload to krok, o którym często się zapomina: systemd buforuje pliki jednostek, więc edycja nie przyniesie efektu bez przeładowania. enable --now jednocześnie włącza usługę przy starcie systemu i uruchamia ją natychmiast. Sprawdź stan:

sudo systemctl status myapp.service
* myapp.service - My application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
   Main PID: 4123 (myapp)

Active: active (running) oraz enabled to stany, które powinny zostać wyświetlone. Aby odczytać wyjście programu, należy poprosić dziennik o wpisy dotyczące tylko tej jednostki:

sudo journalctl -u myapp.service -f

Flaga -f śledzi nowe linie w czasie rzeczywistym, podobnie jak tail -f. Wszystko, co program zapisuje na standardowe wyjście lub standardowe wyjście błędów, trafia tutaj bez konieczności dodatkowej konfiguracji logowania.

Restart po awarii, czyli główny cel konfiguracji

Główną zaletą usługi jest to, że systemd automatycznie restartuje program w przypadku jego zakończenia. Odpowiadają za to dwie linie:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure restartuje program, gdy ten kończy działanie z kodem błędu innym niż zero lub zostaje przerwany przez sygnał awaryjny, taki jak SIGKILL lub SIGSEGV. Poprawne zakończenie lub zatrzymanie za pomocą sygnałów SIGTERM, SIGINT, SIGHUP lub SIGPIPE nie wywołuje restartu. RestartSec=5 wprowadza pięciosekundową przerwę między próbami, dzięki czemu program, który ulega natychmiastowej awarii, nie wpada w nieskończoną pętlę. Należy to zweryfikować poprzez zabicie procesu i obserwację, jak systemd przywraca go do działania. Należy użyć sygnału SIGKILL: domyślny sygnał SIGTERM jest traktowany jako poprawne zatrzymanie, więc on-failure nie spowodowałoby restartu usługi:

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

W ciągu pięciu sekund status pokaże nowy Main PID oraz ponowne active (running). To cała funkcjonalność i właśnie dlatego usługa jest lepszym rozwiązaniem niż pozostawienie programu uruchomionego w tmux lub screen.

Uruchomienie jako użytkownik bez uprawnień i wzmocnienie zabezpieczeń

Usługa działająca z uprawnieniami root może przejąć pełną kontrolę nad serwerem w przypadku wykorzystania luki w oprogramowaniu. Należy uruchamiać ją jako dedykowany użytkownik i zastosować dyrektywy systemd, które ograniczą jej środowisko pracy. Najpierw należy utworzyć konto systemowe bez możliwości logowania i katalogu domowego:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

Następnie należy ustawić User=myapp i dodać linie wzmacniające zabezpieczenia do [Service]:

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Każda z tych linii usuwa uprawnienia, których program nie potrzebuje do działania. NoNewPrivileges=true uniemożliwia procesowi uzyskanie nowych uprawnień, nawet w przypadku użycia plików binarnych z ustawionym bitem setuid. PrivateTmp=true zapewnia prywatny /tmp, niewidoczny dla innych procesów. ProtectSystem=strict ustawia cały system plików w trybie tylko do odczytu, z wyjątkiem ścieżek wskazanych za pomocą ReadWritePaths=. ProtectHome=true całkowicie ukrywa /home przed procesem. Jest to ta sama zasada ograniczonego przywileju, co umieszczenie usługi za firewallem: należy nadać tylko te uprawnienia, które są niezbędne. Jeśli zapoznano się z przewodnikiem dotyczącym zamykania luki w firewallu IPv6 na VPS, jest to lokalna odpowiedź na to samo zagadnienie. W przypadku usługi wystawionej na dostęp z Internetu, należy połączyć te zabezpieczenia z Fail2ban przed SSH oraz firewallem z domyślną polityką odrzucania ruchu.

Zamiast wpisywać wszystkie te parametry ręcznie i ryzykować błąd w dyrektywach, należy wygenerować kompletną, zabezpieczoną jednostkę i skopiować jej treść:

Toolsystemd service and timer generator

Timery: nowoczesny odpowiednik cron

Timer systemd uruchamia usługę zgodnie z harmonogramem i stanowi nowoczesny zamiennik dla zadań cron. Timer składa się z dwóch plików: .service, który wykonuje pracę, oraz .timer, który określa czas wykonania. Załóżmy, że wymagane jest wykonanie kopii zapasowej codziennie o godzinie 3:00. Usługa wykonuje zadanie jednorazowo i kończy działanie:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot informuje systemd, że program uruchamia się, kończy pracę i wyłącza, zamiast pozostawać w pamięci. Timer planuje to działanie:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 oznacza godzinę 3:00 każdego dnia. Dowolne wyrażenie kalendarzowe można przetestować za pomocą systemd-analyze calendar "*-*-* 03:00:00", co potwierdza poprawność parsowania i wyświetla kolejne terminy uruchomienia. Persistent=true uruchamia pominięte zadanie natychmiast po powrocie serwera do pracy, jeśli był on wyłączony o godzinie 3:00, czego cron nie potrafi. Należy zauważyć, że timer włącza się za pomocą timers.target, a nie multi-user.target. Należy włączyć timer, a nie usługę:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers wyświetla wszystkie timery wraz z czasem ich ostatniego i następnego uruchomienia, co pozwala na szybki podgląd harmonogramu zadań. Powyższy generator tworzy sparowane pliki .service oraz .timer po włączeniu trybu timera. W porównaniu z linią w cron, timer zapewnia rzeczywiste logi w journal, te same dyrektywy zabezpieczające co każda inna usługa oraz funkcję nadrabiania pominiętych uruchomień, którą zapewnia Persistent=true. Cron jest wystarczający dla prostych zadań; timer jest lepszym narzędziem, gdy zadanie ma znaczenie krytyczne.

FAQ

Jaka jest różnica między usługą systemd a zadaniem cron?

Usługa utrzymuje działanie długotrwałego procesu: uruchamia się podczas startu systemu, restartuje w razie awarii i zapisuje logi w dzienniku. Zadanie cron wykonuje krótkie polecenie zgodnie z harmonogramem, a następnie kończy pracę. Jeśli wymagane jest planowanie zadań, a jednocześnie logowanie do dziennika, zaostrzenie zabezpieczeń i nadrobienie pominiętych uruchomień, należy użyć timera systemd. Łączy on harmonogram .timer z usługą oneshot i zastępuje crona w większości zadań serwerowych.

Gdzie umieścić plik usługi systemd?

Własne jednostki należy umieszczać w /etc/systemd/system/, nadając im nazwy kończące się na .service. Ten katalog jest przeznaczony dla jednostek dodawanych przez administratora i ma pierwszeństwo przed jednostkami dostarczanymi przez pakiety w /lib/systemd/system/. Po utworzeniu lub edycji pliku w tej lokalizacji należy wykonać sudo systemctl daemon-reload, aby systemd uwzględnił zmiany.

Jak skonfigurować restart usługi w przypadku awarii?

Należy dodać Restart=on-failure oraz RestartSec=5 do sekcji [Service], a następnie wykonać sudo systemctl daemon-reload i zrestartować usługę. systemd ponownie uruchomi program, jeśli zakończy się on kodem innym niż zero lub zostanie przerwany sygnałem awarii, odczekując pięć sekund między próbami. Testowanie można przeprowadzić za pomocą sudo systemctl kill -s SIGKILL myapp.service; sygnał SIGTERM, będący sygnałem domyślnym, jest traktowany jako poprawne zatrzymanie i nie wyzwala on-failure. Należy obserwować systemctl status, aby sprawdzić, czy w ciągu kilku sekund pojawi się nowy PID.

Jak uruchomić usługę systemd jako użytkownik niebędący rootem?

Należy utworzyć konto systemowe za pomocą sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp, a następnie dodać User=myapp do sekcji [Service]. Należy również dodać NoNewPrivileges=true, PrivateTmp=true oraz ProtectSystem=strict, aby proces działał z minimalnym wymaganym zakresem uprawnień. Uruchamianie jako użytkownik bez uprawnień administracyjnych jest najważniejszą pojedynczą zmianą zwiększającą bezpieczeństwo usługi.

Dlaczego usługa nie uruchomiła się?

Należy wykonać systemctl status myapp.service w celu uzyskania podsumowania oraz journalctl -u myapp.service dla pełnego wyjścia. Najczęstsze przyczyny to błędna ścieżka w ExecStart, brakujący plik WorkingDirectory, błąd uprawnień, ponieważ User= nie może odczytać pliku, lub zapomniane sudo systemctl daemon-reload po edycji. Dziennik zawiera własny komunikat o błędzie programu, który zazwyczaj bezpośrednio wskazuje przyczynę problemu.