Top.Mail.Ru

Systemd: создание и управление своими сервисами в Linux

4
Systemd: создание и управление своими сервисами в Linux

Systemd-сервис — это текстовый файл с расширением .service, который описывает как запускать программу, что делать при её падении и в каком порядке стартовать относительно других сервисов. Создали файл, выполнили две команды — и приложение запускается автоматически при каждой загрузке сервера и перезапускается если упало.

Где хранятся unit-файлы

Systemd ищет unit-файлы в нескольких директориях с разным приоритетом:

  • /etc/systemd/system/ — ваши собственные сервисы, наивысший приоритет
  • /lib/systemd/system/ или /usr/lib/systemd/system/ — сервисы установленных пакетов

Всегда создавайте свои сервисы в /etc/systemd/system/. Файлы из /lib/systemd/system/ принадлежат пакетному менеджеру — при обновлении пакета они будут перезаписаны, и ваши изменения потеряются.

Структура unit-файла

Создать файл для нового сервиса:

sudo nano /etc/systemd/system/myapp.service

Базовый шаблон выглядит так:

[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Три секции обязательны. [Unit] описывает сервис и его зависимости. [Service] задаёт как именно его запускать и что делать при сбоях. [Install] определяет, при каком системном состоянии сервис должен становиться активным.

Разбор параметров секции [Service]

Type — как systemd отслеживает процесс

Параметр Type= определяет, каким образом systemd понимает что сервис успешно запустился.

Type=simple. ExecStart запускает главный процесс напрямую, и systemd сразу считает сервис запущенным. Это значение по умолчанию и правильный выбор для большинства современных приложений — веб-серверов, API, ботов.

Type=forking. Используется для традиционных Unix-демонов, которые форкаются: порождают дочерний процесс и завершают родительский. Systemd в этом случае ждёт пока родительский процесс не завершится, и только тогда считает сервис запущенным.

Type=oneshot. Подходит для скриптов которые выполняют одно действие и завершаются — например, скрипт миграции базы данных. Systemd дожидается полного завершения скрипта прежде чем перейти к следующему шагу.

Restart — политика перезапуска

Параметр Restart= определяет, должен ли systemd перезапускать сервис после его завершения, и в каких случаях.

Restart=on-failure. Перезапуск происходит только при ненулевом коде выхода или при получении сигнала завершения работы. Это правильный выбор для подавляющего большинства сервисов — сервис перезапустится после сбоя, но не будет перезапускаться если вы сами остановили его командой.

Restart=always. Сервис перезапускается при любом завершении процесса — в том числе после команды systemctl stop. Здесь кроется частая проблема: если попытаться остановить такой сервис обычной командой stop, он тут же запустится снова. Чтобы остановить его действительно надолго, используйте команду systemctl mask вместо stop.

Restart=no. Сервис никогда не перезапускается автоматически, независимо от причины завершения.

RestartSec — пауза перед перезапуском

Задаёт паузу в секундах перед тем, как systemd попытается перезапустить упавший сервис. Без этой паузы приложение с багом при старте будет перезапускаться тысячи раз в минуту, создавая избыточную нагрузку на процессор и диск.

WorkingDirectory — рабочая директория

Указывает директорию, из которой запускается процесс. Это важно, если приложение обращается к файлам и конфигурациям по относительным путям — без указания рабочей директории такие пути могут не найтись.

User и Group — от чьего имени работает сервис

Определяют пользователя и группу, от имени которых запускается процесс. Никогда не запускайте приложения от root без явной необходимости: если приложение будет скомпрометировано, атакующий получит полный доступ к системе, а не только к файлам этого приложения.

Переменные окружения: как хранить секреты правильно

Существует два способа передать переменные окружения в сервис, и выбор между ними имеет значение для безопасности.

Способ 1: значения прямо в unit-файле

Подходит для несекретных настроек — например, режима работы приложения или номера порта:

[Service]
Environment=NODE_ENV=production
Environment=PORT=3000

Способ 2: вынесенный файл с переменными

Подходит для паролей, ключей API и другой чувствительной информации. Сначала создаётся отдельный файл:

sudo nano /etc/myapp/secrets.env

Содержимое файла:

DB_PASSWORD=надёжный-пароль
API_KEY=abcdef123456

Файлу нужно ограничить права доступа так, чтобы читать его мог только владелец:

sudo chmod 600 /etc/myapp/secrets.env

В unit-файле указывается путь к этому файлу:

[Service]
EnvironmentFile=/etc/myapp/secrets.env

Разница между способами принципиальна для безопасности. Значения указанные через Environment= видны в открытом виде в выводе команды systemctl status и в логах journald — любой пользователь с доступом к этим командам увидит пароль. Файл EnvironmentFile с правами 600 читает только root и процесс самого сервиса — секреты не попадают в статус-команды и логи.

Обязательный порядок действий после изменения файла

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

sudo systemctl daemon-reload

Без выполнения этой команды systemd продолжает использовать кешированную версию файла, загруженную в память при предыдущем запуске. Даже если вы отредактировали .service файл и выполнили команду restart для сервиса — изменения не применятся, потому что systemd попросту не знает что файл на диске изменился.

Запустить сервис и одновременно добавить его в автозапуск можно одной командой:

sudo systemctl enable --now myapp

Флаг --now запускает сервис немедленно и одновременно с этим добавляет его в автозапуск при загрузке системы. Без этого флага сервис будет добавлен в автозапуск, но фактически запустится только при следующей перезагрузке сервера.

В чём разница между enable и start

Эта путаница возникает почти у каждого кто впервые настраивает systemd-сервис.

Команда systemctl start запускает сервис прямо сейчас, единожды. После перезагрузки сервера этот сервис автоматически не запустится — придётся снова выполнять start вручную.

Команда systemctl enable создаёт символическую ссылку в целевой директории /etc/systemd/system/multi-user.target.wants/, благодаря которой сервис будет запускаться при каждой загрузке системы. Сама по себе эта команда сервис не запускает — только настраивает автозапуск на будущее.

Команда systemctl enable --now объединяет оба действия: запускает сервис прямо сейчас и одновременно настраивает автозапуск на будущее. Именно эта команда нужна в подавляющем большинстве случаев.

Зависимости между сервисами: After, Wants, Requires

Здесь тоже есть распространённая ошибка — указать только After= и решить что зависимость между сервисами настроена. На самом деле это не так.

After — определяет только порядок запуска

Директива After=postgresql.service говорит systemd: если оба сервиса запускаются, запусти этот после postgresql. Но если postgresql вообще не запущен на сервере — данный сервис всё равно стартует, просто без гарантированного порядка.

Requires — жёсткая зависимость

Директива Requires=postgresql.service создаёт настоящую зависимость: если postgresql не смог запуститься, этот сервис тоже не запустится, и systemd сообщит об ошибке.

Wants — мягкая зависимость

Директива Wants=postgresql.service заставляет systemd попытаться запустить postgresql вместе с этим сервисом, но если запуск postgresql не удался — сервис всё равно стартует в обычном режиме.

Для большинства веб-приложений, которым база данных действительно необходима для работы, правильная комбинация выглядит так:

[Unit]
After=network.target postgresql.service
Requires=postgresql.service

Если же приложение способно работать в ограниченном режиме без базы данных, используется более мягкий вариант:

[Unit]
After=network.target postgresql.service
Wants=postgresql.service

Просмотр логов сервиса

Весь вывод сервиса — как стандартный поток, так и поток ошибок — автоматически попадает в journald, системный журнал логов.

Посмотреть все записи в логе конкретного сервиса:

journalctl -u myapp

Следить за логом в реальном времени, по мере поступления новых записей:

journalctl -u myapp -f

Посмотреть только последние 50 строк лога:

journalctl -u myapp -n 50

Посмотреть записи за последний час:

journalctl -u myapp --since "1 hour ago"

Посмотреть записи начиная с конкретного момента времени:

journalctl -u myapp --since "2026-07-01 10:00"

Если приложение уже пишет собственные логи в отдельные файлы и дублирование в journald не нужно, вывод можно отключить:

[Service]
StandardOutput=null
StandardError=null

Либо, наоборот, направить вывод напрямую в конкретные файлы:

[Service]
StandardOutput=append:/var/log/myapp.log
StandardError=append:/var/log/myapp-error.log

Как изменить параметры системного сервиса не трогая оригинальный файл

Для сервисов, установленных через пакетный менеджер (например, Nginx), редактировать оригинальный unit-файл напрямую — плохая идея: при следующем обновлении пакета все изменения будут стёрты.

Правильный подход — механизм переопределения. Он создаёт отдельный файл, который дополняет оригинальный, не изменяя его:

sudo systemctl edit nginx

Эта команда откроет текстовый редактор для нового файла override.conf, который будет сохранён в директории /etc/systemd/system/nginx.service.d/. В этот файл нужно добавить только те параметры, которые требуется изменить, например:

[Service]
LimitNOFILE=65535

После сохранения файла нужно применить изменения:

sudo systemctl daemon-reload
sudo systemctl restart nginx

Шпаргалка по основным командам

Создать или отредактировать unit-файл:

sudo nano /etc/systemd/system/myapp.service

Применить изменения после редактирования файла — обязательный шаг, без которого правки не подействуют:

sudo systemctl daemon-reload

Запустить сервис и добавить его в автозапуск одновременно:

sudo systemctl enable --now myapp

Остановить работающий сервис:

sudo systemctl stop myapp

Убрать сервис из автозапуска, не останавливая его если он уже работает:

sudo systemctl disable myapp

Посмотреть текущий статус сервиса — работает ли он, когда был запущен, есть ли ошибки:

sudo systemctl status myapp

Следить за логами сервиса в реальном времени:

journalctl -u myapp -f

Перезапустить сервис — например, после изменения конфигурации приложения:

sudo systemctl restart myapp

Часто задаваемые вопросы

Почему изменения в unit-файле не применяются после restart?

Потому что не был выполнен systemctl daemon-reload. Systemd кеширует unit-файлы в памяти при загрузке. После любого изменения файла нужно явно сказать systemd перечитать конфигурацию заново: sudo systemctl daemon-reload. Только после этого команда restart применит новую конфигурацию к сервису.

Чем отличается Restart=on-failure от Restart=always?

on-failure перезапускает сервис только при ненулевом коде выхода или аварийном завершении процесса. always перезапускает сервис при любом его завершении — в том числе и после команды systemctl stop. Если сервис настроен с Restart=always и его нужно остановить надолго, используйте команду systemctl mask — она блокирует любой запуск сервиса, в отличие от disable, которая только убирает его из автозапуска.

Как передать переменные окружения в systemd-сервис?

Есть два способа. Environment=KEY=value в секции [Service] — для несекретных значений, они видны в открытом виде через systemctl status. EnvironmentFile=/path/to/file — для секретов вроде паролей и ключей API, файл с правами 600 читает только root и процесс самого сервиса.

Как запустить сервис от конкретного пользователя, а не от root?

Укажите директивы User=имя_пользователя и Group=имя_группы в секции [Service] unit-файла. Пользователь должен уже существовать в системе — создать его можно командой useradd. Запускать приложения от root без необходимости не рекомендуется из соображений безопасности.

Как посмотреть все активные сервисы на сервере?

Команда systemctl list-units --type=service --state=running покажет список всех сервисов которые сейчас работают. Команда systemctl list-units --type=service --state=failed покажет сервисы которые не смогли успешно запуститься — это полезно для быстрой диагностики проблем на сервере.

Systemd-сервисы незаменимы для запуска приложений на VPS — они стартуют автоматически после перезагрузки и перезапускаются при падении. На UFO.Hosting каждый сервер работает на Ubuntu с systemd, так что можно сразу создавать сервисы после подключения по SSH.

Похожее

Все статьи
php версии ubuntu

PHP: управление версиями через ondrej PPA на Ubuntu

PPA ondrej/php позволяет устанавливать несколько версий PHP параллельно и переключаться между ними без переустановки системы. Добавить репозиторий — одна команда, установить PHP 8.2 и 8.3 одновременно — ещё две. Ключевой момент который понимают не сразу: смена версии в командной строке…

swap linux

Swap-файл в Linux: как добавить и настроить на VPS

Swap-файл — это область на диске которую Linux использует как дополнительную оперативную память когда физическая RAM заканчивается. Создаётся за 5 команд и не требует перераздела диска. На VPS с 1-2 ГБ RAM swap защищает от OOM Killer — ситуации когда…