Jak uruchomić program jako usługa systemd
Dowiedz się, jak skonfigurować plik .service w /etc/systemd/system/ aby zapewnić automatyczny restart procesu oraz logowanie zdarzeń do journalctl.
Czym jest usługa systemd i dlaczego jest potrzebna
Usługa systemd to mały plik tekstowy, który definiuje sposób uruchamiania programu przez serwer: start podczas rozruchu systemu, restart po awarii oraz przekazywanie wyjścia do dziennika systemowego. To wszystkie zadania usługi. Program uruchomiony ręcznie w sesji SSH przestaje działać po wylogowaniu użytkownika lub restarcie serwera. Program objęty usługą systemd działa w sposób ciągły, ponieważ zarządza nim system, a nie powłoka użytkownika.
systemd jest systemem init w dystrybucjach Ubuntu, Debian, Fedora oraz większości nowoczesnych serwerów Linux. Jest to pierwszy uruchamiany proces, który nadzoruje wszystkie pozostałe procesy. Utworzenie pliku usługi przekazuje program do nadzoru systemowi. Niniejszy poradnik przedstawia minimalną działającą jednostkę, trzy sekcje wymagane w każdej jednostce, metody uruchamiania i odczytywania logów, uruchamianie według harmonogramu za pomocą timerów oraz ograniczanie uprawnień usługi do niezbędnego minimum.
Najmniejsza działająca usługa
Plik usługi znajduje się w /etc/systemd/system/, ma rozszerzenie .service i wymaga tylko kilku linii. Utwórz plik dla programu 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.targetJest to kompletna, działająca jednostka. ExecStart to polecenie do wykonania. WantedBy=multi-user.target oznacza uruchomienie usługi po przejściu serwera w tryb wieloużytkownika, co umożliwia jej automatyczne uruchamianie podczas bootowania. Pozostałe parametry służą jedynie optymalizacji.
Trzy sekcje i ich przeznaczenie
Każdy plik jednostki jest podzielony na sekcje umieszczone w nawiasach kwadratowych. Usługa wykorzystuje trzy z nich.
[Unit] opisuje usługę oraz jej zależności. Dwie najczęściej używane linie to:
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription to etykieta tekstowa widoczna w systemctl status. After=network-online.target instruuje systemd, aby nie uruchamiał programu przed nawiązaniem połączenia sieciowego, co jest istotne dla aplikacji otwierających porty lub nawiązujących połączenia wychodzące.
[Service] określa sposób uruchamiania programu. W tej sekcji znajduje 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=infoUser=myapp uruchamia program na nieuprzywilejowanym koncie zamiast na koncie root, co jest kluczowym ustawieniem dla bezpieczeństwa. Restart=on-failure oraz RestartSec=5 posiadają własne sekcje poniżej, ponieważ stanowią główny powód tworzenia usług.
[Install] określa działania podejmowane po włączeniu usługi:
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target odpowiada za rejestrację usługi podczas uruchamiania systemu po wykonaniu polecenia systemctl enable. Bez sekcji [Install] usługa może zostać uruchomiona ręcznie, ale nie wystartuje automatycznie po ponownym uruchomieniu 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ę jedną komendą:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.serviceKrok daemon-reload jest często pomijany: systemd buforuje pliki jednostek, więc edycja nie zostanie uwzględniona bez przeładowania. Komenda enable --now jednocześnie umożliwia automatyczne uruchamianie usługi przy starcie systemu oraz uruchamia ją natychmiast. Aby to sprawdzić, należy wykonać:
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)Pożądane są stany Active: active (running) oraz enabled. Aby odczytać wyjście programu, należy wyświetlić logi dla konkretnej jednostki w journal:
sudo journalctl -u myapp.service -f-f śledzi nowe wpisy w czasie rzeczywistym, podobnie jak tail -f. Wszystkie dane zapisane przez program na standardowym wyjściu (stdout) lub standardowym błędzie (stderr) trafiają tutaj bez konieczności dodatkowej konfiguracji logowania.
Restart on failure, powód, dla którego tu jesteś
Główną zaletą usługi jest automatyczne restartowanie programu przez systemd po jego awarii. Odpowiadają za to dwie linie:
[Service]
Restart=on-failure
RestartSec=5Restart=on-failure restartuje program, gdy zakończy on działanie z kodem wyjściowym innym niż 0 lub ulegnie awarii wskutek sygnału, takiego jak SIGKILL lub SIGSEGV. Czyste zakończenie lub zatrzymanie przez sygnały SIGTERM, SIGINT, SIGHUP lub SIGPIPE nie wyzwala restartu. RestartSec=5 wprowadza pięciosekundowe opóźnienie między próbami, co zapobiega zapętleniu programu, który ulega natychmiastowej awarii. Można to sprawdzić, zabijając proces i obserwując, jak systemd go przywraca. Należy użyć SIGKILL: domyślny SIGTERM jest traktowany jako czyste zatrzymanie, więc on-failure nie zrestartuje usługi:
sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.serviceW ciągu pięciu sekund status ponownie wyświetli nowy Main PID oraz active (running). To jest cała funkcjonalność; dzięki niej usługa jest lepszym rozwiązaniem niż uruchamianie programu w tmux lub screen.
Uruchom proces jako użytkownik nieuprzywilejowany i utwardź go
Usługa uruchamiana jako root może wykonać dowolną operację na serwerze w przypadku przejęcia kontroli nad programem. Należy uruchamiać usługę jako oddzielny użytkownik i zastosować dyrektywy systemd ograniczające jej uprawnienia. Najpierw należy utworzyć konto systemowe bez możliwości logowania i bez katalogu domowego:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myappNastępnie należy ustawić User=myapp oraz dodać linie hardeningu do [Service]:
[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=trueKażda linia usuwa funkcjonalność niepotrzebną programowi. NoNewPrivileges=true uniemożliwia procesowi uzyskanie nowych uprawnień, nawet poprzez binaria setuid. PrivateTmp=true zapewnia prywatny /tmp niewidoczny dla innych procesów. ProtectSystem=strict ustawia cały system plików w trybie read-only, z wyjątkiem ścieżek zdefiniowanych przez ReadWritePaths=. ProtectHome=true całkowicie ukrywa /home. Jest to podejście oparte na zasadzie najmniejszych uprawnień, analogiczne do umieszczania usługi za firewallem: należy przydzielać tylko niezbędne zasoby. Jeśli przeczytano poradnik dotyczący zamykania luk firewalla IPv6 na VPS, powyższe działania stanowią lokalną część tej samej koncepcji. W przypadku usługi wystawionej na publiczny internet, należy połączyć to utwardzanie z rozwiązaniem Fail2ban przed SSH oraz firewallem działającym w trybie default-deny.
Zamiast wpisywać wszystkie parametry ręcznie, co może prowadzić do błędów, należy wygenerować kompletny, utwardzony plik jednostki i go skopiować:
Timers: nowoczesny cron
Timer systemd uruchamia usługę według harmonogramu. Jest to nowoczesny zamiennik zadań cron. Timer składa się z dwóch plików: .timer wykonującego zadanie oraz .service określającego czas uruchomienia. Przykładowo, aby wykonywać kopię zapasową codziennie o godzinie 3:00, należy stworzyć usługę, która wykonuje zadanie i kończy działanie:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shType=oneshot informuje systemd, że program kończy działanie, zamiast pozostawać w pamięci. Timer planuje uruchomienie:
# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetOnCalendar=*-*-* 03:00:00 oznacza godzinę 3:00 każdego dnia. Wyrażenia kalendarzowe można przetestować za pomocą systemd-analyze calendar "*-*-* 03:00:00", co pozwala sprawdzić poprawność parsowania i wyświetlić kolejne terminy uruchomienia. Persistent=true uruchamia pominięte zadanie natychmiast po uruchomieniu serwera, jeśli system był wyłączony o godzinie 3:00; cron nie posiada takiej funkcji. Należy pamiętać, że timer należy aktywować za pomocą timers.target, a nie multi-user.target. Aktywuje się timer, a nie usługę:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers wyświetla wszystkie timery wraz z terminem następnego i poprzedniego uruchomienia, co pozwala szybko sprawdzić kolejny termin wykonania zadania. Powyższy generator tworzy sparowane pliki .service i .timer automatycznie po włączeniu trybu timer. W porównaniu do cron, timer zapewnia logi w journalu, te same dyrektywy hardeningu co każda usługa oraz funkcję nadrabiania pominiętych zadań, którą zapewnia Persistent=true. Cron jest wystarczający dla prostych zadań; timer jest lepszym narzędziem w przypadku zadań krytycznych.
FAQ
Jaka jest różnica między usługą systemd a zadaniem cron?
Usługa utrzymuje działający program: uruchamia się podczas bootowania, restartuje po błędzie i loguje dane do journal. Zadanie cron wykonuje krótkie polecenie według harmonogramu, a następnie kończy działanie. W przypadku potrzeby harmonogramu przy jednoczesnym zachowaniu logów w journal, hardeningu oraz funkcji nadrabiania zaległych uruchomień, należy użyć timer systemd. Łączy on harmonogram .timer z usługą oneshot i zastępuje cron w większości zadań serwerowych.
Gdzie umieścić plik usługi systemd?
Własne jednostki należy umieścić w /etc/systemd/system/, nadając im nazwę zakończoną rozszerzeniem .service. Katalog ten służy do przechowywania jednostek dodanych przez administratora i ma priorytet nad jednostkami dostarczanymi przez pakiety w /lib/systemd/system/. Po utworzeniu lub edycji pliku należy uruchomić sudo systemctl daemon-reload, aby systemd odczytał zmiany.
Jak wymusić restart usługi po awarii?
Należy dodać Restart=on-failure oraz RestartSec=5 do sekcji [Service], a następnie uruchomić sudo systemctl daemon-reload i zrestartować usługę. systemd uruchamia ponownie program po wyjściu z kodem innym niż 0 lub po awarii wywołanej sygnałem, zachowując pięciosekundową przerwę między próbami. Można to przetestować za pomocą sudo systemctl kill -s SIGKILL myapp.service — sygnał SIGTERM, będący domyślnym sygnałem, jest traktowany jako poprawne zatrzymanie i nie wyzwala on-failure — należy monitorować 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 dodać NoNewPrivileges=true, PrivateTmp=true oraz ProtectSystem=strict, aby proces posiadał minimalne niezbędne uprawnienia. Uruchamianie procesu jako użytkownik nieuprzywilejowany jest najważniejszą zmianą zwiększającą bezpieczeństwo usługi.
Dlaczego usługa nie uruchomiła się?
Należy uruchomić systemctl status myapp.service w celu uzyskania podsumowania oraz journalctl -u myapp.service w celu uzyskania pełnego wyjścia. Najczęstsze przyczyny to błędna ścieżka w ExecStart, brakujący WorkingDirectory, błąd uprawnień wynikający z braku możliwości odczytu pliku przez User= lub brak wykonania sudo systemctl daemon-reload po edycji. Journal zawiera własne komunikaty błędów programu, które zazwyczaj bezpośrednio wskazują przyczynę problemu.