SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Як запустити програму як systemd service на VPS

Налаштуйте systemd service для автозапуску програми на VPS, перезапуску після збою та журналювання через journal. Додайте timer і обмежте права.

Що таке systemd service і навіщо він потрібен

systemd service — це невеликий текстовий файл, який визначає, як сервер має запускати програму: запускати її під час завантаження, перезапускати після аварійного завершення та надсилати її вивід до системного журналу. Це все, що він робить. Програма, яку ви запускаєте вручну в SSH-сеансі, завершується одразу після виходу із сеансу або перезавантаження сервера. Програма, оформлена як systemd service, продовжує працювати, оскільки нею керує сам сервер, а не ваша оболонка.

systemd — це init-система в Ubuntu, Debian, Fedora та більшості сучасних Linux-серверів. Це перший процес, який запускається, і процес, що контролює всі інші. Так було не завжди, і матеріал про те, як systemd замінив попередні init-скрипти, варто прочитати після того, як ви зрозумієте призначення unit-файлу. Створюючи service-файл, ви передаєте свою програму цьому supervisor. У цьому посібнику описано найменший робочий unit, три секції, які має кожен unit, увімкнення та перегляд його журналів, запуск за розкладом за допомогою timer, а також обмеження прав, щоб програма працювала з мінімально необхідними привілеями.

Найпростіший робочий сервіс

Файл сервісу зберігається в /etc/systemd/system/, має розширення .service і містить лише кілька рядків. Створіть такий файл для програми в /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

Це повністю готовий до роботи unit. ExecStart — команда для запуску. WantedBy=multi-user.target означає запустити її після переходу сервера до штатного режиму роботи для кількох користувачів. Саме це забезпечує запуск під час завантаження. Усе інше — додаткове налаштування.

Три секції та призначення кожної

Кожен unit-файл поділено на секції у квадратних дужках. Сервіс використовує три секції.

[Unit] описує сервіс і його зв’язки. Два рядки, які ви використовуватимете найчастіше:

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

Description — це зрозуміла назва, яку ви бачите в systemctl status. After=network-online.target повідомляє systemd не запускати вашу програму, доки мережа не буде доступна. Це важливо для всього, що прив’язується до порту або встановлює вихідне з’єднання.

[Service] визначає спосіб запуску програми. Тут розміщується більшість налаштувань:

[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 запускає програму від непривілейованого облікового запису, а не від root. Це найважливіший для безпеки рядок. Тут немає рядка Type=, тому systemd використовує значення simple і припускає, що процес ExecStart працює у foreground. Якщо програма переходить у background через fork, потрібно вказати правильне значення Type= відповідно до способу її запуску, інакше unit повідомить, що він активний, хоча фактичний daemon уже завершив роботу. Restart=on-failure і RestartSec=5 мають власну секцію нижче, оскільки саме через них найчастіше створюють сервіс.

[Install] визначає, що відбувається під час увімкнення сервісу:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target додає сервіс до процесу завантаження, коли ви виконуєте systemctl enable. Без секції [Install] сервіс можна запустити вручну, але після перезавантаження він не запуститься автоматично.

Увімкніть сервіс і стежте за його роботою

Після створення або редагування будь-якого unit-файлу перезавантажте конфігурацію systemd, щоб systemd прочитав зміни. Потім одним кроком увімкніть і запустіть сервіс:

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

daemon-reload — це крок, про який часто забувають: systemd кешує unit-файли, тому зміни не застосовуються, доки ви не перезавантажите конфігурацію. enable --now одночасно вмикає сервіс для запуску під час завантаження системи та запускає його негайно. Перевірте стан:

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) і enabled — саме ті результати, які потрібні. Щоб переглянути вивід програми, запитайте в журналі лише записи для цього unit:

sudo journalctl -u myapp.service -f

-f стежить за новими рядками в міру їх появи, подібно до tail -f. Усе, що програма записує до стандартного виводу або стандартного потоку помилок, потрапляє сюди без додаткового налаштування журналювання.

Перезапуск у разі помилки — причина, з якої ви тут

Основна перевага сервісу полягає в тому, що systemd перезапускає вашу програму після її завершення. Для цього потрібні два рядки:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure перезапускає програму, якщо вона завершується з ненульовим кодом або аварійно завершується через сигнал, наприклад SIGKILL чи SIGSEGV. Нормальне завершення або зупинка сигналом SIGTERM, SIGINT, SIGHUP чи SIGPIPE не запускає перезапуск. RestartSec=5 робить паузу п’ять секунд між спробами, щоб програма, яка завершується одразу після запуску, не зациклювалася в безперервному циклі. Перевірте це, завершивши процес і спостерігаючи, як systemd запускає його знову. Використайте SIGKILL: типовий SIGTERM вважається штатною зупинкою, тому on-failure не перезапустить сервіс:

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

Протягом п’яти секунд у статусі з’являється новий Main PID, а active (running) знову має значення. Це вся функціональність, і саме тому сервіс кращий за залишення програми запущеною в tmux або screen.

Запускайте його від непривілейованого користувача та ізолюйте

Сервіс, який працює від root, може виконати будь-які дії на сервері, якщо в програмі буде знайдено вразливість. Запускайте його від окремого користувача та додайте до systemd кілька директив для ізоляції. Спочатку створіть системний обліковий запис без можливості входу та без домашнього каталогу:

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

Потім задайте User=myapp і додайте директиви захисту до [Service]:

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

Кожен рядок обмежує можливості, які програмі не потрібні. NoNewPrivileges=true не дає процесу отримати нові привілеї, зокрема через бінарний файл setuid. PrivateTmp=true надає йому приватний /tmp, який не бачить жоден інший процес. ProtectSystem=strict робить усю файлову систему доступною лише для читання, крім кількох шляхів, які ви вкажете через ReadWritePaths=. ProtectHome=true повністю приховує від нього /home. Це той самий принцип найменших привілеїв, що й розміщення сервісу за firewall: надавайте йому лише необхідні можливості. Якщо ви читали посібник про закриття прогалини у правилах IPv6 firewall на VPS, це локальна частина того самого підходу. Для сервісу, доступного з інтернету, поєднайте це посилення захисту з Fail2ban перед SSH і firewall із політикою заборони за замовчуванням.

Щоб не вводити все вручну та не переплутати директиви, згенеруйте повний unit із посиленим захистом і скопіюйте його:

Toolsystemd service and timer generator

Таймери: сучасна заміна cron

Таймер systemd запускає service за розкладом і є сучасною заміною завдання cron. Таймер складається з двох файлів: .service, який виконує роботу, і .timer, який визначає час запуску. Наприклад, потрібно щодня виконувати резервне копіювання о 3am. service виконує завдання один раз і завершує роботу:

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

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

Type=oneshot повідомляє systemd, що програма запускається, завершує роботу й виходить, а не залишається постійно запущеною. Таймер задає розклад:

# /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 означає 3am щодня. Перевіряйте будь-який календарний вираз за допомогою systemd-analyze calendar "*-*-* 03:00:00". Команда підтверджує, що вираз правильно розібрано, і виводить наступні заплановані моменти запуску. Persistent=true запускає пропущене завдання одразу після відновлення роботи сервера, якщо о 3am він був вимкнений. cron цього не вміє. Зверніть увагу: таймер вмикають через timers.target, а не через multi-user.target. Увімкніть таймер, а не service:

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

list-timers показує всі таймери з часом наступного та останнього запуску. Так можна одразу побачити, коли завдання буде запущено наступного разу. Наведений вище генератор створює для вас пов’язані .service і .timer під час увімкнення режиму таймера. Порівняно з рядком cron таймер надає реальні журнали в журналі systemd, ті самі директиви hardening, що й будь-який service, а також механізм виконання пропущених запусків, який забезпечує Persistent=true. cron і далі підходить для простих завдань. Але якщо завдання важливе, краще використовувати таймер.

FAQ

У чому різниця між systemd service і cron job?

Service підтримує роботу довготривалої програми: запускає її під час завантаження, перезапускає після збою та записує журнали до journal. Cron job запускає коротку команду за розкладом і завершує роботу. Якщо потрібен розклад, а також журнали journal, hardening і запуск пропущених завдань, використовуйте systemd timer. Він поєднує розклад .timer із service oneshot і замінює cron для більшості серверних завдань.

Де розмістити файл systemd service?

Розміщуйте власні units у /etc/systemd/system/. Їхні імена мають закінчуватися на .service. Цей каталог призначений для units, доданих адміністратором, і має вищий пріоритет, ніж units, які пакети постачають у /lib/systemd/system/. Після створення або редагування файлу виконайте sudo systemctl daemon-reload, щоб systemd застосував зміни.

Як налаштувати перезапуск service після збою?

Додайте Restart=on-failure і RestartSec=5 до секції [Service], потім виконайте sudo systemctl daemon-reload і перезапустіть service. systemd повторно запускає програму, якщо вона завершується з ненульовим кодом або завершується через сигнал збою. Між спробами systemd очікує 5 секунд. Перевірте це за допомогою sudo systemctl kill -s SIGKILL myapp.service. SIGTERM, сигнал за замовчуванням, вважається штатною зупинкою та не запускає on-failure. Моніторте systemctl status: протягом кількох секунд там має з’явитися новий PID.

Як запустити systemd service від імені користувача, відмінного від root?

Створіть системний обліковий запис за допомогою sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp, потім додайте User=myapp до секції [Service]. Додайте NoNewPrivileges=true, PrivateTmp=true і ProtectSystem=strict, щоб процес працював із мінімально необхідними правами доступу. Запуск від імені непривілейованого користувача — найважливіша окрема зміна, яку можна внести для підвищення безпеки service.

Чому service не вдалося запустити?

Виконайте systemctl status myapp.service, щоб отримати зведення, і journalctl -u myapp.service, щоб переглянути повний вивід. Найпоширеніші причини: неправильний шлях у ExecStart, відсутній WorkingDirectory, помилка доступу, оскільки User= не може прочитати файл, або пропущений sudo systemctl daemon-reload після редагування. У журналі міститься власне повідомлення програми про помилку. Зазвичай воно безпосередньо вказує на проблему.