Systemd
systemd — система инициализации и менеджер служб Linux. Обычно она запускается как процесс с PID 1, активирует системные службы, управляет зависимостями и расписаниями, а вместе с systemd-journald собирает журналы.
Основные команды:
systemctl— управление юнитами и системой;journalctl— просмотр журналов;systemd-analyze— анализ загрузки и проверка unit-файлов.
Проверка:
ps -p 1 -o pid,comm,args
systemctl --versionЮниты systemd
Юнит — объект, которым управляет systemd. Тип определяется расширением имени.
| Тип | Назначение | Пример |
|---|---|---|
.service |
Служба или процесс | sshd.service |
.timer |
Запуск юнита по расписанию | backup.timer |
.socket |
Сокет и активация по обращению | docker.socket |
.target |
Группа юнитов или состояние системы | multi-user.target |
.mount |
Точка монтирования | home.mount |
.path |
Реакция на изменение пути | import.path |
.device |
Устройство ядра | dev-sda.device |
.swap |
Область подкачки | dev-sda2.swap |
Просмотр юнитов:
systemctl list-units
systemctl list-units --type=service
systemctl list-unit-files
systemctl --failedОсновные состояния:
active— юнит активен;inactive— неактивен;failed— завершился с ошибкой;enabled— включён автозапуск;disabled— автозапуск не настроен;static— запускается как зависимость, обычныйenableне предусмотрен;masked— запуск полностью заблокирован.
systemctl is-active nginx.service
systemctl is-enabled nginx.service
systemctl is-failed nginx.serviceУправление сервисами
Запуск, остановка и перезапуск:
sudo systemctl start nginx.service
sudo systemctl stop nginx.service
sudo systemctl restart nginx.serviceПеречитать конфигурацию приложения, если служба поддерживает это действие:
sudo systemctl reload nginx.service
sudo systemctl reload-or-restart nginx.serviceСтатус:
systemctl status nginx.service
systemctl --no-pager status nginx.serviceАвтозапуск:
sudo systemctl enable nginx.service
sudo systemctl disable nginx.service
systemctl is-enabled nginx.serviceenable обычно не запускает службу немедленно. Для запуска и включения одной командой:
sudo systemctl enable --now nginx.serviceОстановить и отключить:
sudo systemctl disable --now nginx.serviceСбросить отметку failed после устранения причины ошибки:
sudo systemctl reset-failed nginx.serviceПолностью запретить запуск:
sudo systemctl mask example.service
sudo systemctl unmask example.servicedisable убирает автозапуск, но допускает ручной запуск. mask запрещает и ручной запуск, и запуск как зависимости.
Расположение unit-файлов
| Путь | Назначение |
|---|---|
/etc/systemd/system/ |
Локальные юниты и переопределения администратора |
/run/systemd/system/ |
Временные юниты до перезагрузки |
/usr/lib/systemd/system/ |
Юниты, установленные пакетами |
/lib/systemd/system/ |
Пакетные юниты в некоторых дистрибутивах |
~/.config/systemd/user/ |
Пользовательские юниты |
Локальные файлы в /etc/systemd/system/ имеют приоритет над пакетными. Не следует редактировать пакетные unit-файлы напрямую: обновление пакета может удалить изменения.
Посмотреть итоговую конфигурацию:
systemctl cat nginx.service
systemctl show nginx.serviceПользовательские юниты:
systemctl --user daemon-reload
systemctl --user enable --now my-app.service
systemctl --user status my-app.serviceСобственный service unit
Допустим, программа запускается командой:
/usr/local/bin/my-app --config /etc/my-app/config.tomlФайл /etc/systemd/system/my-app.service:
[Unit]
Description=My application
Wants=network-online.target
After=network-online.target
[Service]
Type=exec
User=my-app
Group=my-app
WorkingDirectory=/var/lib/my-app
EnvironmentFile=-/etc/my-app/my-app.env
ExecStart=/usr/local/bin/my-app --config /etc/my-app/config.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
[Install]
WantedBy=multi-user.targetПрименение:
sudo systemctl daemon-reload
sudo systemctl enable --now my-app.service
systemctl status my-app.servicedaemon-reload перечитывает unit-файлы, но не перезапускает работающую службу. После изменения настроек обычно нужен отдельный restart.
Секции
[Unit] содержит описание и зависимости:
[Unit]
Description=Краткое описание
Documentation=man:my-app(1)
Requires=postgresql.service
After=postgresql.service[Service] описывает процесс:
[Service]
Type=exec
User=my-app
Group=my-app
ExecStart=/usr/local/bin/my-app
Restart=on-failure[Install] определяет, куда systemctl enable добавит ссылки:
[Install]
WantedBy=multi-user.targetЧастые параметры [Service]:
| Параметр | Назначение |
|---|---|
Type= |
Способ определения запуска и готовности |
User=, Group= |
Пользователь и группа процесса |
WorkingDirectory= |
Рабочий каталог |
ExecStart= |
Основная команда |
ExecStartPre= |
Команда перед запуском |
ExecReload= |
Перезагрузка конфигурации |
Environment= |
Переменная окружения |
EnvironmentFile= |
Файл переменных окружения |
Restart= |
Политика перезапуска |
RestartSec= |
Задержка перед перезапуском |
TimeoutStartSec= |
Ограничение времени запуска |
TimeoutStopSec= |
Ограничение времени остановки |
Основные значения Type=:
simple— процессExecStartсчитается основным;exec— запуск успешен после выполнения системного вызоваexecve();oneshot— одноразовая команда выполняется и завершается;forking— приложение самостоятельно уходит в фон;notify— приложение сообщает systemd о готовности.
Новую службу желательно оставлять в foreground, а не отправлять в фон самостоятельно.
ExecStart и shell
systemd не запускает ExecStart= через shell автоматически. Пайпы, >, &&, glob-шаблоны и $(...) напрямую не обрабатываются.
Предпочтительно вынести сложную команду в скрипт:
ExecStart=/usr/local/libexec/process-dataЕсли shell действительно нужен:
ExecStart=/bin/bash -c '/usr/bin/cat /var/log/app.log | /usr/bin/grep ERROR'В ExecStart= следует использовать абсолютные пути. Найти путь:
command -v my-appПеременные окружения
[Service]
Environment="APP_MODE=production"
Environment="LOG_LEVEL=info"
EnvironmentFile=-/etc/my-app/my-app.envПример файла:
APP_MODE=production
LOG_LEVEL=infoПрефикс - у EnvironmentFile= разрешает отсутствие файла. Это не полноценный Bash-скрипт: не стоит помещать туда export, подстановки команд и shell-логику.
Проверка unit-файла
systemd-analyze verify /etc/systemd/system/my-app.service
systemctl cat my-app.service
systemctl show my-app.service -p FragmentPath -p User -p ExecStartПереопределение установленного юнита
Создать drop-in-файл:
sudo systemctl edit nginx.serviceПример /etc/systemd/system/nginx.service.d/override.conf:
[Service]
Restart=on-failure
RestartSec=10sПосле сохранения:
sudo systemctl daemon-reload
sudo systemctl restart nginx.serviceЧтобы заменить список ExecStart=, сначала сбрасывают старое значение:
[Service]
ExecStart=
ExecStart=/usr/sbin/nginx -g "daemon off;" -c /etc/nginx/custom.confУдалить локальные переопределения и вернуть пакетный вариант:
sudo systemctl revert nginx.serviceЗависимости между юнитами
Зависимость запуска и порядок запуска — разные понятия.
[Unit]
Requires=postgresql.service
Wants=redis.service
After=postgresql.service redis.service
Before=reporting.serviceRequires=— сильная зависимость;Wants=— мягкая зависимость;After=— запускать текущий юнит после указанного;Before=— запускать текущий юнит до указанного;PartOf=— распространять остановку и перезапуск связанного юнита;BindsTo=— тесно связать жизненный цикл юнитов.
After= не запускает другой юнит. Если он должен и запускаться, и быть готов раньше, используют оба параметра:
[Unit]
Requires=postgresql.service
After=postgresql.serviceСетевой пример:
[Unit]
Wants=network-online.target
After=network-online.targetnetwork.target не гарантирует получение адреса или доступность внешнего сервера. Даже после network-online.target приложение должно уметь переживать временные сетевые ошибки.
Просмотр зависимостей:
systemctl list-dependencies my-app.service
systemctl list-dependencies --reverse my-app.service
systemd-analyze critical-chain my-app.serviceSocket units
.socket позволяет systemd открыть сокет и запустить службу при первом подключении.
/etc/systemd/system/my-echo.socket:
[Unit]
Description=Socket for echo service
[Socket]
ListenStream=127.0.0.1:9000
[Install]
WantedBy=sockets.target/etc/systemd/system/my-echo.service:
[Unit]
Description=Socket-activated echo service
[Service]
ExecStart=/usr/local/bin/my-echo-server
StandardInput=socketsudo systemctl daemon-reload
sudo systemctl enable --now my-echo.socket
systemctl status my-echo.socketПо совпадающему имени my-echo.socket активирует my-echo.service. Приложение должно поддерживать получение открытого сокета в выбранном режиме.
Логи через journalctl
Журнал службы:
journalctl -u nginx.service
journalctl -u nginx.service -n 100
journalctl -u nginx.service -fТекущая и предыдущая загрузки:
journalctl -b
journalctl -b -1
journalctl --list-bootsСообщения ядра:
journalctl -k -bФильтрация по времени:
journalctl --since today
journalctl --since "1 hour ago"
journalctl --since "2026-09-11 09:00" --until "2026-09-11 10:00"Фильтрация по приоритету:
journalctl -p err
journalctl -p warning..emergФорматы:
journalctl -u my-app.service -o short-iso
journalctl -u my-app.service -o verbose
journalctl -u my-app.service -o json-pretty
journalctl -u my-app.service --no-pagerОбъём и очистка архивных журналов:
journalctl --disk-usage
sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-size=500MТипичная диагностика:
systemctl status my-app.service
journalctl -u my-app.service -b -n 100 --no-pager
systemctl cat my-app.service
systemctl show my-app.service -p Result -p ExecMainStatusTargets и уровни загрузки
.target объединяет другие юниты и представляет состояние системы.
| Target | Назначение |
|---|---|
default.target |
Target по умолчанию |
multi-user.target |
Многопользовательский текстовый режим |
graphical.target |
Графическая среда |
rescue.target |
Режим восстановления |
emergency.target |
Минимальная аварийная оболочка |
network-online.target |
Ожидание настроенной сети |
timers.target |
Группа timers |
sockets.target |
Группа sockets |
systemctl get-default
sudo systemctl set-default multi-user.target
sudo systemctl set-default graphical.targetПерейти к target сейчас:
sudo systemctl isolate multi-user.targetisolate может остановить лишние для нового target службы и пользовательские процессы. Особенно осторожно применяйте его при удалённом подключении.
Типичное соответствие старым runlevels:
| Runlevel | systemd target |
|---|---|
| 0 | poweroff.target |
| 1 | rescue.target |
| 2, 3, 4 | multi-user.target |
| 5 | graphical.target |
| 6 | reboot.target |
Systemd timers как замена cron
Timer запускает другой юнит по расписанию. Обычно backup.timer активирует backup.service.
Преимущества:
- журналы через journal;
- зависимости и отдельный пользователь;
- календарные и интервальные расписания;
- выполнение пропущенной задачи после включения;
- случайная задержка для распределения нагрузки;
- ограничения ресурсов и защита службы.
Service /etc/systemd/system/backup-app.service:
[Unit]
Description=Back up application data
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-app
User=root
Group=root
Nice=10Timer /etc/systemd/system/backup-app.timer:
[Unit]
Description=Run application backup daily
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10m
Unit=backup-app.service
[Install]
WantedBy=timers.targetАктивация и проверка:
sudo systemctl daemon-reload
sudo systemctl enable --now backup-app.timer
systemctl list-timers --all
systemctl status backup-app.timerРучной тест задачи:
sudo systemctl start backup-app.service
journalctl -u backup-app.service -n 100OnCalendar=
# Ежедневно в 02:30
OnCalendar=*-*-* 02:30:00
# Каждый понедельник в 09:00
OnCalendar=Mon *-*-* 09:00:00
# Каждый час
OnCalendar=hourly
# Первого числа месяца
OnCalendar=*-*-01 00:15:00Проверка выражения:
systemd-analyze calendar 'Mon *-*-* 09:00:00'
systemd-analyze calendar --iterations=5 'daily'Интервальные timers
[Timer]
OnBootSec=5min
OnUnitActiveSec=1hOnActiveSec=— от активации timer;OnBootSec=— от загрузки ОС;OnStartupSec=— от запуска менеджера systemd;OnUnitActiveSec=— от последней активации связанного юнита;OnUnitInactiveSec=— от перехода связанного юнита в неактивное состояние.
Полезные параметры:
[Timer]
Persistent=true
AccuracySec=1min
RandomizedDelaySec=15minPersistent=true позволяет календарному timer выполнить после старта пропущенную задачу. Обычно это один компенсирующий запуск, а не отдельный запуск за каждый пропущенный период.
Включать для расписания нужно именно .timer; связанный .service будет запускаться им автоматически.
Шаблонные юниты
Шаблон worker@.service позволяет создать несколько экземпляров:
[Unit]
Description=Worker instance %i
After=network.target
[Service]
Type=exec
User=worker
ExecStart=/usr/local/bin/worker --queue=%i
Restart=on-failure
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now worker@emails.service
sudo systemctl enable --now worker@images.service%i заменяется именем экземпляра: emails или images.
Ограничение и защита службы
[Service]
User=my-app
Group=my-app
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/my-app
MemoryMax=512M
CPUQuota=50%
TasksMax=100
LimitNOFILE=65536NoNewPrivileges=trueзапрещает получение новых привилегий;PrivateTmp=trueсоздаёт изолированные временные каталоги;ProtectSystem=ограничивает запись в системные пути;ProtectHome=ограничивает доступ к домашним каталогам;ReadWritePaths=разрешает запись в выбранные места;MemoryMax=,CPUQuota=,TasksMax=ограничивают ресурсы.
Оценка защиты:
systemd-analyze security my-app.serviceОграничения следует добавлять постепенно: слишком строгие параметры могут нарушить работу приложения.
Типичные ошибки
После изменения unit-файла забыли перечитать конфигурацию:
sudo systemctl daemon-reload
sudo systemctl restart my-app.serviceenable не запустил службу сейчас:
sudo systemctl enable --now my-app.serviceПрограмма работает вручную, но не через systemd. Проверьте пользователя, права, абсолютные пути, WorkingDirectory=, переменные окружения и журнал. Профили .bashrc и .profile для системной службы обычно не загружаются.
systemctl status my-app.service
journalctl -u my-app.service -b
systemctl show my-app.service -p User -p Group -p Environment -p WorkingDirectoryTimer не срабатывает:
systemctl status backup-app.timer
systemctl list-timers --all
systemctl status backup-app.service
journalctl -u backup-app.timer -u backup-app.serviceПроверьте, что включён .timer, существует связанный .service, а системное время и часовой пояс верны:
timedatectlКраткая памятка
# Службы
systemctl status name.service
sudo systemctl start name.service
sudo systemctl stop name.service
sudo systemctl restart name.service
sudo systemctl enable --now name.service
sudo systemctl disable --now name.service
# Unit-файлы
systemctl cat name.service
systemctl show name.service
sudo systemctl edit name.service
sudo systemctl daemon-reload
systemd-analyze verify /etc/systemd/system/name.service
# Ошибки и журнал
systemctl --failed
sudo systemctl reset-failed name.service
journalctl -u name.service -b
journalctl -u name.service -n 100
journalctl -u name.service -f
# Зависимости и загрузка
systemctl list-dependencies name.service
systemd-analyze critical-chain
systemctl get-default
# Timers
systemctl list-timers --all
sudo systemctl enable --now task.timer
systemd-analyze calendar 'daily'Рекомендации
- Не редактируйте пакетные unit-файлы напрямую; применяйте
systemctl edit. - Различайте
startиenable, а такжеdisableиmask. - После изменения unit-файлов выполняйте
daemon-reload. - Используйте абсолютные пути и не рассчитывайте на интерактивное shell-окружение.
- Запускайте службу от непривилегированного пользователя, если права
rootне нужны. - Различайте зависимости (
Wants=,Requires=) и порядок (After=,Before=). - Для диагностики сочетайте
systemctl status,journalctl,systemctl catиsystemctl show. - Для расписаний проверяйте и
.timer, и запускаемый им.service. - Усиливайте защиту и вводите ограничения ресурсов постепенно, проверяя приложение после изменений.