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: управление версиями через ondrej PPA на Ubuntu
PPA ondrej/php позволяет устанавливать несколько версий PHP параллельно и переключаться между ними без переустановки системы. Добавить репозиторий — одна команда, установить PHP 8.2 и 8.3 одновременно — ещё две. Ключевой момент который понимают не сразу: смена версии в командной строке…
Swap-файл в Linux: как добавить и настроить на VPS
Swap-файл — это область на диске которую Linux использует как дополнительную оперативную память когда физическая RAM заканчивается. Создаётся за 5 команд и не требует перераздела диска. На VPS с 1-2 ГБ RAM swap защищает от OOM Killer — ситуации когда…