Как запустить программу как systemd сервис
Инструкция по созданию unit-файла для автоматического запуска программы при загрузке VPS, настройки перезапуска при сбое и работы с systemd journal.
Что такое сервис systemd и зачем он нужен
Сервис systemd — это небольшой текстовый файл, который определяет правила запуска программы: запуск при загрузке системы, перезапуск при сбое и отправку вывода в системный журнал. Это все функции сервиса. Программа, запущенная вручную в сессии SSH, завершается сразу после вашего выхода или перезагрузки сервера. Программа, оформленная как сервис systemd, продолжает работать, так как управление ею передается системе, а не вашей оболочке (shell).
systemd является системой инициализации в Ubuntu, Debian, Fedora и большинстве современных Linux-серверов. Это первый запускаемый процесс, который контролирует работу всех остальных процессов. Создавая файл сервиса, вы передаете управление программой этому контроллеру. В данном руководстве описана минимально рабочая структура юнита, три обязательные секции, способы включения и чтения логов, запуск по расписанию с помощью таймера, а также методы ограничения привилегий для обеспечения безопасности.
Минимальный рабочий сервис
Файл сервиса находится в /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Это готовый к работе модуль. ExecStart — команда для запуска. WantedBy=multi-user.target означает запуск после перехода сервера в режим многопользовательской работы, что обеспечивает автозапуск при загрузке. Остальные параметры служат для тонкой настройки.
Три раздела и их назначение
Каждый unit-файл разделен на разделы, заключенные в квадратные скобки. В сервисе используются три раздела.
[Unit] описывает сервис и его зависимости. Две наиболее часто используемые строки:
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription — это человекочитаемое название, которое вы видите в 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=infoUser=myapp запускает программу от имени непривилегированного пользователя вместо root; это самая важная строка для обеспечения безопасности. Restart=on-failure и RestartSec=5 вынесены в отдельные разделы ниже, так как именно из-за них большинство пользователей создают сервисы.
[Install] определяет действия при включении сервиса:
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target связывает сервис с процессом загрузки при выполнении команды systemctl enable. Без раздела [Install] сервис можно запустить вручную, но он не запустится автоматически после перезагрузки.
Включите и наблюдайте
После создания или редактирования любого unit-файла выполните reload systemd, чтобы изменения вступили в силу. Затем включите и запустите сервис одной командой:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.servicedaemon-reload — это шаг, который часто пропускают: systemd кэширует unit-файлы, поэтому изменения не применятся до выполнения reload. Команда 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. Чтобы просмотреть вывод программы, запросите в journal логи только этого unit:
sudo journalctl -u myapp.service -f-f отслеживает новые строки по мере их появления, подобно tail -f. Любые данные, которые ваша программа записывает в standard output или standard error, попадают сюда без дополнительной настройки логирования.
Перезапуск при сбое — причина вашего обращения
Основное преимущество использования service заключается в том, что systemd перезапускает вашу программу после завершения её работы. Для этого используются две строки:
[Service]
Restart=on-failure
RestartSec=5Restart=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). Это и есть основная функция, благодаря которой использование service эффективнее, чем запуск программы в 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. Это реализует принцип наименьших привилегий, аналогичный использованию межсетевого экрана: предоставляйте сервису только необходимые ресурсы. Если вы изучили руководство по закрытию уязвимостей IPv6 firewall на VPS, то это локальная реализация того же подхода. Для сервиса, имеющего доступ к интернету, используйте эту защиту вместе с Fail2ban перед SSH и межсетевым экраном с политикой запрета по умолчанию (default-deny).
Чтобы не вводить все команды вручную и не допустить ошибок в директивах, сгенерируйте готовый защищенный unit-файл и скопируйте его:
Таймеры: современная альтернатива cron
Таймер systemd запускает службу по расписанию и является современной заменой cron. Таймер состоит из двух файлов: .service, который выполняет работу, и .timer, который определяет время запуска. Допустим, вам нужно выполнять резервное копирование каждый день в 3 часа утра. Служба выполняет задачу один раз и завершается:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shType=oneshot сообщает systemd, что программа запускается, завершает работу и выходит, а не остается в памяти. Таймер планирует запуск:
# /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 означает 3 часа утра каждый день. Вы можете проверить любое календарное выражение с помощью systemd-analyze calendar "*-*-* 03:00:00"; эта команда подтверждает корректность парсинга и выводит время следующих запусков. Persistent=true запускает пропущенную задачу сразу после включения сервера, если в 3 часа утра он был выключен. Cron не обладает такой возможностью. Обратите внимание, что таймер включается через timers.target, а не через multi-user.target. Включайте таймер, а не службу:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers отображает все таймеры с указанием времени следующего и последнего запуска, что позволяет быстро узнать время следующего выполнения задачи. Генератор, описанный выше, автоматически создает пары .service и .timer при включении режима таймеров. В отличие от строк cron, таймеры предоставляют полноценные логи в journal, те же директивы безопасности, что и у любой службы, а также механизм компенсации пропущенных запусков, который обеспечивает Persistent=true. Cron подходит для простых задач; таймеры — более эффективный инструмент для критически важных задач.
FAQ
В чем разница между службой systemd и задачей cron?
Служба обеспечивает непрерывную работу программы: она запускается при загрузке системы, перезапускается при сбоях и записывает логи в journal. Задача cron выполняет короткую команду по расписанию и завершается. Если вам требуется планировщик, но также нужны логи в journal, механизмы защиты (hardening) и выполнение пропущенных задач, используйте systemd timer. Он сочетает расписание .timer со службой oneshot и заменяет cron для большинства серверных задач.
Куда поместить файл службы systemd?
Размещайте пользовательские юниты в /etc/systemd/system/, используя имя, заканчивающееся на .service. Этот каталог предназначен для юнитов, добавленных администратором; он имеет приоритет над юнитами из пакетов в /lib/systemd/system/. После создания или редактирования файла выполните sudo systemctl daemon-reload, чтобы systemd применил изменения.
Как настроить перезапуск службы при сбое?
Добавьте Restart=on-failure и RestartSec=5 в секцию [Service], затем выполните sudo systemctl daemon-reload и перезапустите службу. systemd перезапускает программу, если она завершилась с ненулевым кодом или аварийно завершилась по сигналу, выдерживая паузу в пять секунд между попытками. Проверьте работу с помощью sudo systemctl kill -s SIGKILL myapp.service — сигнал SIGTERM является стандартным, считается корректным завершением и не вызывает on-failure; убедитесь, что systemctl status показывает новый PID через несколько секунд.
Как запустить службу systemd от имени пользователя без прав root?
Создайте системную учетную запись с помощью sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp, затем добавьте User=myapp в секцию [Service]. Добавьте NoNewPrivileges=true, PrivateTmp=true и ProtectSystem=strict, чтобы процесс имел минимально необходимый уровень доступа. Запуск от имени непривилегированного пользователя — это самое важное действие для повышения безопасности службы.
Почему служба не запустилась?
Выполните systemctl status myapp.service для получения сводки и journalctl -u myapp.service для получения полного вывода. Наиболее частые причины: неверный путь в ExecStart, отсутствие WorkingDirectory, ошибка прав доступа (User= не может прочитать файл) или отсутствие команды sudo systemctl daemon-reload после редактирования. В journal содержится сообщение об ошибке самой программы, в котором обычно прямо указана причина проблемы.