kai.kzLab

Сервис systemd не запускается: status, journalctl, коды выхода и зависимости

Команда KAI Lab · · 8 мин чтения

Если сервис systemd не запускается, начните с systemctl status имя: там результат запуска (Result:), код выхода процесса и последние строки журнала, а полный журнал с момента загрузки покажет journalctl -u имя -b. Код вида status=203/EXEC или status=217/USER значит, что systemd не смог даже запустить программу, а status=1/FAILURE — что программа запустилась и сама завершилась с ошибкой, и причину надо искать в её собственных сообщениях.

Ниже — порядок проверки и разбор типичных ошибок. Перезапускать сервис по кругу в надежде, что «само пройдёт», бессмысленно: после нескольких быстрых неудач systemd сам перестанет пытаться.

Симптом → команда → что смотреть

СимптомКомандаЧто смотреть
Сервис в failedsystemctl status appActive: и Result:, status= у Main PID:, последние строки журнала
Нужна полная картинаjournalctl -u app -bпервую ошибку после запуска, а не последнюю строку
status=203/EXECsystemctl cat appпуть в ExecStart=, право на исполнение файла
status=217/USERid имясуществует ли пользователь из User=
status=200/CHDIRls -ld каталогесть ли каталог из WorkingDirectory= и доступ к нему
При запуске пишет changed on disksystemctl daemon-reloadsystemd работает со старой версией unit-файла
Result: start-limit-hitsystemctl reset-failed appсначала причину падений, потом сброс счётчика
A dependency job ... failedsystemctl --failedкакая из зависимостей упала
Unit ... is maskedsystemctl unmask appкто и зачем замаскировал сервис
Сомнения в unit-файлеsystemd-analyze verifyопечатки в ключах и секциях

Шаг 1. systemctl status

$ systemctl status orders-worker
× orders-worker.service - Orders worker
     Loaded: loaded (/etc/systemd/system/orders-worker.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Thu 2026-09-24 03:12:41 UTC; 2min 3s ago
   Duration: 812ms
    Process: 48213 ExecStart=/opt/orders/bin/worker --config /etc/orders/worker.yml (code=exited, status=1/FAILURE)
   Main PID: 48213 (code=exited, status=1/FAILURE)
        CPU: 790ms

Sep 24 03:12:41 app-01 worker[48213]: FATAL: open /etc/orders/worker.yml: permission denied
Sep 24 03:12:41 app-01 systemd[1]: orders-worker.service: Main process exited, code=exited, status=1/FAILURE
Sep 24 03:12:41 app-01 systemd[1]: orders-worker.service: Failed with result 'exit-code'.

Как это читать:

  • Loaded: — откуда загружен unit-файл и включён ли автозапуск (enabled). Если здесь not-found, masked или bad-setting, до запуска дело не дошло.
  • Active: failed (Result: exit-code) — сервис упал из-за ненулевого кода выхода. Другие частые результаты: timeout (не уложился в TimeoutStartSec=, по умолчанию 90 секунд), signal (процесс убит сигналом), core-dump, oom-kill, start-limit-hit.
  • Main PID: ... status=1/FAILURE — код выхода самой программы.
  • Внизу — хвост журнала, по умолчанию десять строк. Длинные строки обрезаются, целиком их покажет systemctl status -l.

Здесь причина видна сразу: программа не может прочитать свой конфиг. Проверяем, от кого она работает и какие права у файла:

$ ls -l /etc/orders/worker.yml
-rw------- 1 root root 1184 Sep 24 02:58 /etc/orders/worker.yml
$ systemctl show -p User orders-worker
User=orders

Файл читает только root, а сервис запущен от orders. Исправление — дать доступ группе сервиса, а не открывать файл всем: sudo chown root:orders /etc/orders/worker.yml и sudo chmod 640 /etc/orders/worker.yml.

Шаг 2. journalctl: полная история

status показывает только хвост. Если сервис перезапускался несколько раз или упал давно, нужен журнал целиком:

$ journalctl -u orders-worker -b                   # всё о сервисе с момента загрузки
$ journalctl -u orders-worker --since "1 hour ago"
$ journalctl -u orders-worker -b -p warning        # только предупреждения и ошибки
$ journalctl -xeu orders-worker                    # конец журнала с пояснениями

Ищите первую ошибку после запуска. Последние строки обычно описывают последствия, вроде Failed with result, а причина выше. Флаг -x добавляет пояснения из каталога сообщений systemd, -e сразу переходит в конец журнала.

Если приложение пишет лог в свой файл, а не в stdout, в журнале останутся только записи systemd о коде выхода. Тогда открывайте файл приложения. У многих демонов есть и собственная проверка конфигурации, которая называет строку с ошибкой: nginx -t, sshd -t, apachectl configtest.

Коды выхода: кто упал — systemd или программа

Коды от 200 и выше выставляет сам systemd: он не смог подготовить окружение, и до запуска программы дело не дошло. Коды 1–7 описаны в спецификации LSB, но их возвращает сама программа, и она может вкладывать в них свой смысл.

КодЧто значит на практике
1/FAILUREпрограмма запустилась и упала: читайте её сообщения
2/INVALIDARGUMENTчаще всего неверные аргументы: опечатка в ExecStart=, неизвестный флаг
200/CHDIRнет каталога из WorkingDirectory= или нет прав на него
203/EXECне удалось выполнить файл: его нет, нет бита x или неверный интерпретатор в первой строке скрипта
217/USERпользователя из User= нет в системе
226/NAMESPACEне удалось настроить изоляцию: часто не существует путь из ReadWritePaths= или похожей настройки

Так выглядит 203/EXEC в журнале:

$ journalctl -u orders-worker -b -p warning -o cat
orders-worker.service: Failed at step EXEC spawning /opt/orders/bin/worker: No such file or directory
orders-worker.service: Failed with result 'exit-code'.

-o cat убирает время и имя хоста — удобно, когда важен только текст. Типичные причины: файла нет по этому пути (опечатка, релиз не докатился, симлинк ведёт в пустоту), у файла нет права на исполнение, у скрипта неверная строка #!. Отдельная ловушка — скрипт, сохранённый с окончаниями строк Windows: ядро ищет интерпретатор /bin/bash с невидимым \r на конце и возвращает ту же No such file or directory.

А так — 217/USER:

$ journalctl -u orders-worker -b -p warning -o cat
orders-worker.service: Failed to determine user credentials: No such process
orders-worker.service: Failed at step USER spawning /opt/orders/bin/worker: No such process
orders-worker.service: Failed with result 'exit-code'.

No such process здесь сбивает с толку: так systemd сообщает, что пользователь не найден. Проверка — id orders.

Ещё одна ловушка. Для Type=simple, типа по умолчанию, systemctl start вернёт успех, даже если бинарника нет или пользователя из User= не существует, — это прямо написано в документации. Сервис упадёт через долю секунды, а команда запуска промолчит. Поэтому после старта всегда смотрите status, а в своих unit-файлах выбирайте Type=exec: с ним systemctl start сразу сообщит об ошибке.

Правили unit-файл, а ничего не изменилось: daemon-reload

systemd читает unit-файлы при загрузке и по команде daemon-reload. Если отредактировать файл и сразу сделать restart, запустится старая версия, и systemd об этом предупредит:

$ sudo systemctl restart orders-worker
Warning: The unit file, source configuration file or drop-ins of orders-worker.service changed on disk. Run 'systemctl daemon-reload' to reload units.
$ sudo systemctl daemon-reload
$ sudo systemctl restart orders-worker

Порядок после любой ручной правки: daemon-reload, потом restart. Исключение — systemctl edit orders-worker: команда создаёт drop-in override.conf в каталоге orders-worker.service.d и перечитывает конфигурацию сама. Итоговую конфигурацию со всеми drop-in показывает systemctl cat:

$ systemctl cat orders-worker
# /etc/systemd/system/orders-worker.service
[Unit]
Description=Orders worker

[Service]
User=orders
WorkingDirectory=/opt/orders
ExecStart=/opt/orders/bin/worker --config /etc/orders/worker.yml

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/orders-worker.service.d/override.conf
[Service]
Environment=LOG_LEVEL=debug

Опечатку в ключе systemd не считает фатальной: пишет предупреждение и игнорирует строку. Если ошибиться в ExecStart=, сервис окажется вовсе без команды запуска. Проверяйте файл до перезапуска:

$ sudo systemd-analyze verify /etc/systemd/system/orders-worker.service
/etc/systemd/system/orders-worker.service:7: Unknown key name 'ExecStar' in section 'Service', ignoring.
orders-worker.service: Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing.

Лимит запусков: start-limit-hit

С Restart=on-failure упавший сервис перезапускается, а пауза RestartSec= по умолчанию — всего 100 мс. При этом systemd по умолчанию разрешает не больше пяти запусков за 10 секунд. Сервис, который падает сразу после старта, исчерпывает лимит за секунду: в журнале появится Start request repeated too quickly., статус станет failed (Result: start-limit-hit), и перезапуски прекратятся.

Сначала найдите в журнале причину падений. Потом сбросьте счётчик и запустите:

$ sudo systemctl reset-failed orders-worker
$ sudo systemctl start orders-worker

Чтобы временная проблема — например, база ещё поднимается — не исчерпывала лимит мгновенно, задайте паузу побольше: RestartSec=5s. Пока сервис ждёт очередного перезапуска, status показывает Active: activating (auto-restart). Тот же цикл «упал — перезапустился» в контейнерах разобран в статьях контейнер Docker постоянно перезапускается и CrashLoopBackOff в Kubernetes.

Зависимости: сервис ждёт другой

Requires= и Wants= говорят, какие юниты запустить вместе с сервисом, а After= — в каком порядке. Это разные вещи: без After= зависимые юниты стартуют одновременно. Если указаны Requires= и After=, а зависимость не поднялась, сервис не запустится:

$ sudo systemctl start orders-worker
A dependency job for orders-worker.service failed. See 'journalctl -xe' for details.
$ systemctl --failed --plain --no-legend
db-migrate.service loaded failed failed Orders DB migrations

Чинить нужно упавшую зависимость, а не сам сервис. Wants= мягче: сервис запустится, даже если зависимость не поднялась, — документация systemd советует выбирать его чаще, чем Requires=.

Сервис замаскирован или не найден

$ sudo systemctl start orders-worker
Failed to start orders-worker.service: Unit orders-worker.service is masked.

mask связывает unit с /dev/null, чтобы его нельзя было запустить никаким способом. Обычно это делают сознательно, поэтому перед systemctl unmask выясните, кто и зачем. Сообщение Unit orders-worker.service not found. означает, что файла нет или в имени опечатка: проверьте systemctl list-unit-files 'orders*'.

Руками запускается, из systemd — нет

Частая ситуация: из консоли программа работает, а как сервис падает. Отличается окружение:

  • пользователь: systemd запускает от User=, а вы проверяли от своего или от root;
  • рабочий каталог: без WorkingDirectory= системный сервис стартует в /, и относительные пути ломаются;
  • переменные: ваш .bashrc сервис не читает, переменные для него задают через Environment= или EnvironmentFile=;
  • песочница: настройки вроде ProtectSystem= не дают писать туда, куда пишет программа.

Бывает, что сервис ни при чём. nginx не стартует с ошибкой bind() to 0.0.0.0:80 failed (98: Address already in use) — порт держит другой процесс, разбор в статье порт уже занят. Сервис не может создать pid-файл или временный файл — проверьте, не закончилось ли место на диске.

Во втором месяце симулятора DevOps KAI Lab есть тикет «nginx не стартует после переезда сертификатов»: сайт лежит, и причину нужно найти по статусу, журналу и конфигурации, а не перезапуском наугад. Консоль отвечает реальными выводами команд, так что порядок из этой статьи можно пройти руками.

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

Почему systemctl start ничего не вывел, а сервис не работает? Для Type=simple systemd считает сервис запущенным сразу после создания процесса, ещё до запуска программы. Если бинарника нет или пользователь не существует, команда вернёт успех, а сервис упадёт через долю секунды. Смотрите systemctl status после каждого старта или используйте Type=exec.

Что значит status=203/EXEC? systemd не смог выполнить файл из ExecStart=: его нет по указанному пути, у него нет права на исполнение или у скрипта неверный интерпретатор в первой строке.

Нужно ли делать daemon-reload после каждой правки unit-файла? После правки файлов руками — да. systemctl edit перечитывает конфигурацию сам.

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

Что делать, если в journalctl о сервисе пусто? Проверьте, куда пишет приложение: в свой файл, в syslog или в stdout. Если в журнале только строки systemd о коде выхода, приложение пишет в другое место или упало раньше, чем успело что-то записать.

Источники

  • man7.org: systemctl(1) — status, cat, edit, daemon-reload, reset-failed, mask.
  • man7.org: journalctl(1) — -u, -b, -p, -x, -e, -o.
  • man7.org: systemd.exec(5) — раздел Process Exit Codes, WorkingDirectory=, User=.
  • man7.org: systemd.service(5) — Type=simple и Type=exec, Restart=, RestartSec=, TimeoutStartSec=.
  • man7.org: systemd.unit(5) — Requires=, Wants=, After=, StartLimitIntervalSec=, drop-in файлы.
  • man7.org: systemd-system.conf(5) — лимит запусков и таймауты по умолчанию.
  • man7.org: systemd-analyze(1) — verify.
  • systemd. Исходный код — тексты сообщений Failed at step, Start request repeated too quickly, changed on disk.

Читать дальше