LinuxRuLinux без лишней теории
Диагностика systemd

Упавшая служба systemd: поиск причины и исправление

Редакция LinuxRu6 мин чтенияИнструкции

Упавшую службу systemd находят командой systemctl --failed, причину смотрят в systemctl status и journalctl -u имя -b, а после исправления сбрасывают состояние через systemctl reset-failed. Код в строке status сразу подсказывает, где ошибка: в программе, в юнит-файле или в нехватке памяти. Ниже порядок действий и таблица кодов.

Упавшая служба systemd: поиск причины и исправление
Фото: Software: Arch Linux and systemd developers Screenshot: Paowee, LGPL, Wikimedia Commons
Содержание
  1. Поиск упавших
  2. systemctl status
  3. Журнал
  4. Коды ошибок
  5. Исправление
  6. Перезапуски
  7. Таймауты
  8. OOM и дампы
  9. Частые вопросы

Как найти службы в состоянии failed

Обычно о проблеме узнают по красной строке [FAILED] Failed to start ... при загрузке или по тому, что сайт, VPN или принтер внезапно не работают. Первая команда при любом подозрении:

systemctl --failed

Она выводит все юниты, которые сейчас в состоянии failed: службы, точки монтирования, сокеты, таймеры. Если список пуст, системные службы в порядке. Пользовательские службы (PipeWire, Syncthing, контейнеры Podman от имени пользователя) живут в отдельном менеджере, их проверяют с ключом --user:

systemctl --user --failed
# проверить конкретную службу в скрипте
systemctl is-failed nginx.service

Команда is-failed печатает состояние и возвращает код 0, если служба упала. Это удобно для простого мониторинга через cron или таймер. Общие приёмы управления службами (запуск, остановка, автозагрузка) собраны в статье про systemctl, а здесь разберём именно разбор аварий.

Что рассказывает systemctl status

systemctl status nginx.service

Вывод выглядит громоздко, но читать нужно три места.

  • Строка Active. Например, Active: failed (Result: exit-code) since .... В скобках указан тип сбоя: exit-code, signal, timeout, core-dump или start-limit-hit.
  • Строка Main PID или Process. Там видно, какая команда завершилась и с каким кодом: code=exited, status=1/FAILURE.
  • Последние строки журнала. Внизу status показывает около десяти последних сообщений службы. Часто причина видна прямо здесь: опечатка в конфиге, занятый порт, отсутствующий файл.

Если строки обрезаны по ширине экрана, добавьте -l и отключите пейджер: systemctl status -l --no-pager nginx. Узнать результат без лишнего текста, например для скрипта, можно через свойства юнита:

systemctl show -p Result,ExecMainStatus,NRestarts nginx.service

Журнал службы через journalctl

Десяти строк в status часто мало. Полный журнал службы за текущую загрузку:

journalctl -u nginx.service -b
# сразу в конец, с пояснениями systemd
journalctl -xeu nginx.service
# только ошибки всех служб за эту загрузку
journalctl -b -p err
# прошлая загрузка, если служба упала перед перезагрузкой
journalctl -u nginx.service -b -1
# за последний час
journalctl -u nginx.service --since "1 hour ago"

Ищите первое сообщение об ошибке, а не последнее. Последние строки обычно говорят только "Failed with result exit-code", а настоящая причина на несколько строк выше. Если журнал пуст или обрывается, проверьте, не ограничен ли его размер слишком жёстко: настройка хранения описана в статье про journald.

Типы сбоев и коды выхода

По коду в строке status можно сразу понять, куда копать.

Что видно в statusЗначениеКуда смотреть
status=1/FAILUREпрограмма сама завершилась с ошибкойжурнал программы, её конфиг
status=203/EXECне удалось запустить файл из ExecStartпуть, права на исполнение, первая строка скрипта
status=200/CHDIRнет каталога WorkingDirectoryпуть в юните
status=217/USERнет пользователя из User=создан ли пользователь
status=226/NAMESPACEне удалось настроить изоляциюReadWritePaths, ProtectSystem и несуществующие пути
Result: signal, SIGKILLпроцесс убит, часто OOM killerjournalctl -k, память
Result: core-dumpпрограмма упала с дампомcoredumpctl
Result: timeoutне уложилась в TimeoutStartSecType= и время старта
Result: start-limit-hitслишком много перезапусков подрядпервую ошибку в журнале

Коды от 200 и выше выставляет сам systemd, ещё до запуска программы. Значит, проблема в юнит-файле или окружении, а не в коде приложения.

Исправляем конфиг, юнит и зависимости

Конфиг программы

Многие демоны умеют проверять собственный конфиг без запуска. Сделайте это до рестарта, иначе служба снова упадёт:

sudo nginx -t
sudo sshd -t
sudo apachectl configtest

Ошибка в конфиге SSH особенно неприятна на удалённом сервере, поэтому после правок по статье о защите SSH всегда проверяйте его через sshd -t и держите открытой вторую сессию.

Юнит-файл

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

systemctl cat myapp.service
sudo systemd-analyze verify /etc/systemd/system/myapp.service

Пакетные юниты в /usr/lib/systemd/system править напрямую нельзя: обновление пакета перезапишет изменения. Используйте дополнения, которые лягут в /etc/systemd/system/имя.service.d/:

sudo systemctl edit myapp.service
sudo systemctl daemon-reload
sudo systemctl restart myapp.service

Команда systemctl edit сама выполняет перечитывание конфигурации после сохранения, но лишний daemon-reload не повредит, если файлы правились вручную.

Зависимости

Сообщение Dependency failed for ... означает, что упала не сама служба, а то, что она требует: точка монтирования, сеть, другая служба. Цепочку покажет команда systemctl list-dependencies myapp.service. Чинить нужно первый упавший юнит в цепочке. Частый пример: несуществующий диск в /etc/fstab валит local-fs.target и вместе с ним половину загрузки.

Служба падает по кругу: Restart и лимиты

Для сетевых служб удобно, чтобы systemd поднимал их после сбоя сам. В дополнении к юниту это выглядит так:

[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5

[Service]
Restart=on-failure
RestartSec=5s

Здесь служба перезапускается через 5 секунд после аварии, но не больше пяти раз за пять минут. Обратите внимание: параметры лимита относятся к секции [Unit], а не [Service]. Это частая ошибка, и тогда systemd молча использует значения по умолчанию.

Когда лимит исчерпан, служба получает статус start-limit-hit, и обычный systemctl start откажется её запускать. Сначала исправьте причину, затем сбросьте счётчик:

sudo systemctl reset-failed myapp.service
sudo systemctl start myapp.service

Без аргумента reset-failed очищает состояние failed у всех юнитов. Это удобно после серии экспериментов, чтобы systemctl --failed показывал только свежие проблемы.

Таймауты и неверный Type

Если в status написано Result: timeout, служба не успела сообщить о готовности. Причин две. Первая: программе действительно нужно много времени, например базе данных после аварийного выключения. Тогда увеличьте TimeoutStartSec= в дополнении к юниту. Вторая и более частая: тип запуска не соответствует поведению программы. Если в юните Type=forking, а программа не уходит в фон, или Type=notify, а программа не умеет отправлять уведомление, systemd ждёт сигнала, который никогда не придёт.

Медленную загрузку из-за конкретной службы видно в анализаторе:

systemd-analyze blame | head -20
systemd-analyze critical-chain

Если для вашей программы юнит пишется с нуля, начните с Type=simple или Type=exec: при них не нужны ни уход в фон, ни уведомления. Для контейнеров готовые юниты удобнее генерировать через Podman Quadlet.

Падение по сигналу: нехватка памяти и дампы

Строка Result: signal с SIGKILL почти всегда означает, что процесс убил OOM killer ядра или systemd-oomd. Проверить:

journalctl -k -b | grep -i -E "out of memory|oom"
journalctl -u systemd-oomd -b

Лечение: ограничить аппетит программы, добавить памяти или настроить swap и zram. Для служб, которые не должны съедать всю систему, есть параметр MemoryMax= в юните.

Если программа упала с ошибкой сегментации, systemd-coredump сохраняет дамп:

coredumpctl list
coredumpctl info myapp

В выводе info есть стек вызовов. Его стоит приложить к отчёту об ошибке для разработчиков, а для пакетов из репозитория сначала проверьте, нет ли уже обновления.

Перед тем как лезть в юнит, всегда выполняйте systemctl cat. Нередко выясняется, что кто-то полгода назад создал дополнение в /etc/systemd/system/имя.service.d/ и забыл о нём. Откатить все локальные изменения юнита к пакетной версии можно командой sudo systemctl revert имя.service.

Полное описание параметров Restart, Type и таймаутов есть в справке systemd.service на freedesktop.org. Если служба вам не нужна вовсе и только засоряет список, отключите её: sudo systemctl disable --now имя, а для полной блокировки запуска используйте systemctl mask.

Частые вопросы

Как посмотреть все упавшие службы?

Команда systemctl --failed покажет системные юниты в состоянии failed, а systemctl --user --failed пользовательские.

Что означает status=203/EXEC?

systemd не смог запустить файл из ExecStart: неверный путь, нет права на исполнение или ошибка в первой строке скрипта.

Как убрать состояние failed после исправления?

Выполните sudo systemctl reset-failed имя.service. Без имени команда очистит состояние у всех юнитов.

Что делать при start-limit-hit?

Найдите первую ошибку в journalctl -u имя -b, исправьте её, затем выполните reset-failed и запустите службу снова.

Как вернуть юнит к исходной версии?

Команда sudo systemctl revert имя.service удаляет локальные дополнения и копии юнита в /etc и возвращает версию из пакета.

Документация и источники

Читайте также

Команды проверены на актуальных версиях дистрибутивов на момент обновления. Перед изменением системных настроек сделайте резервную копию.