Порядок диагностики failed unit
Если сервис перешёл в состояние failed, не меняйте права «наугад» и не отключайте защитные механизмы. Сначала определите этап сбоя: systemd не смог подготовить окружение, команда из ExecStart не выполнилась, приложение само завершилось с ошибкой либо не запустилась зависимость.
- Посмотрите состояние через
systemctl status. - Прочитайте полный журнал командой
journalctl -u. - Проверьте загруженный unit-файл, пути, пользователя и окружение.
- Повторите команду от имени сервисного пользователя.
- После исправления выполните проверку,
daemon-reloadи перезапуск.
Шаг 1. Читаем systemctl status
sudo systemctl status myapp.service --no-pager -l
В выводе важны строки Loaded, Active, Process и последние сообщения журнала. Loaded показывает путь к прочитанному unit-файлу. В Active видны состояние и результат, например failed (Result: exit-code). Строка процесса содержит команду, PID и поля code=/status=. Вывод status ограничен, поэтому используйте его как краткую сводку, а не как замену журналу. :contentReference[oaicite:0]{index=0}
После устранения ошибки при необходимости сбросьте лимит неудачных запусков:
sudo systemctl reset-failed myapp.service
Шаг 2. Ищем причину в journalctl
sudo journalctl -u myapp.service -b -n 100 --no-pager
sudo journalctl -u myapp.service --since "10 minutes ago" --no-pager
Ключ -u фильтрует записи по юниту, -b оставляет текущую загрузку системы, -n ограничивает число строк, а --since отделяет свежую попытку от старых ошибок. Ищите первую содержательную строку: «файл не найден», «отказано в доступе», «не существует пользователь», «не удалось перейти в каталог», ошибку конфигурации или сообщение самого приложения. :contentReference[oaicite:1]{index=1}
Строка Failed to start описывает итог; причина обычно находится выше.
Как понимать exit codes
code=exited, status=1 обычно означает, что программа была запущена, но сама вернула ненулевой код. Статусы systemd помогают распознать сбой до нормального запуска процесса:
| Симптом | Вероятная причина | Что проверить |
|---|---|---|
203/EXEC | Не выполнена команда | Путь, наличие файла, право исполнения, формат и shebang |
200/CHDIR | Недоступен рабочий каталог | WorkingDirectory и права на путь |
217/USER | Не применён пользователь | Существует ли User |
216/GROUP | Не применена группа | Существует ли Group |
status=1 или иной код приложения | Ошибка внутри программы | Аргументы, конфигурацию, окружение и логи приложения |
start-limit-hit | Слишком много неудачных запусков | Первую ошибку, затем reset-failed |
Эти коды относятся к этапам подготовки процесса systemd. :contentReference[oaicite:2]{index=2}
Шаг 3. Проверяем загруженную конфигурацию
sudo systemctl cat myapp.service
sudo systemctl show myapp.service -p FragmentPath -p DropInPaths
sudo systemd-analyze verify /etc/systemd/system/myapp.service
systemctl cat показывает основной файл и drop-in-настройки, а systemd-analyze verify выявляет синтаксические ошибки и неизвестные директивы.
ExecStart и пути
Для предсказуемого запуска указывайте полные пути к интерпретатору, бинарному файлу и файлам приложения. systemd может разрешать простое имя программы через собственный путь поиска, но не использует интерактивное окружение вашей SSH-оболочки. Поэтому команда, работающая вручную, может не найтись в сервисе. :contentReference[oaicite:3]{index=3}
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Конвейеры, перенаправления и && не обрабатываются оболочкой автоматически. Предпочтительно запускать программу напрямую. Когда shell действительно нужен, вызывайте его явно:
ExecStart=/bin/sh -c '/usr/bin/example --check && /usr/bin/example --run'
User, Group и WorkingDirectory
Проверьте существование учётной записи и доступ пользователя к исполняемому файлу, конфигурации, рабочему каталогу и каталогу данных:
getent passwd myapp
getent group myapp
namei -l /opt/myapp/venv/bin/python
sudo -u myapp test -x /opt/myapp/venv/bin/python
sudo -u myapp test -r /etc/myapp/myapp.env
sudo -u myapp test -w /var/lib/myapp
Нужно право прохода через родительские каталоги. WorkingDirectory должен существовать до запуска и быть доступен пользователю.
Не применяйте
chmod 777и не отключайте SELinux, AppArmor или изоляцию unit-файла. Определите конкретный недоступный ресурс и выдайте минимальные права владельцу или группе. Для системы контроля доступа исправьте метку, профиль либо разрешающее правило.
EnvironmentFile и переменные
Проверьте путь, права чтения и формат файла. EnvironmentFile содержит присваивания переменных, а не полноценный интерактивный shell-скрипт. Не рассчитывайте на загрузку .bashrc, алиасы или подстановку команд.
EnvironmentFile=/etc/myapp/myapp.env
Environment="APP_MODE=production"
APP_PORT=8080
APP_DATA=/var/lib/myapp
Запись EnvironmentFile=-/etc/myapp/myapp.env делает файл необязательным; применяйте её только осознанно.
Пример unit-файла
[Unit]
Description=MyApp application service
After=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Пользователь, каталоги, виртуальное окружение и файл переменных должны существовать заранее.
Шаг 4. Повторяем запуск от имени сервиса
Проверяйте команду не от root, а с теми же правами и рабочим каталогом:
sudo -u myapp -H sh -c '
cd /opt/myapp || exit 1
set -a
. /etc/myapp/myapp.env
set +a
exec /opt/myapp/venv/bin/python /opt/myapp/app.py
'
Тест выявляет проблемы прав, каталога, переменных и конфигурации, хотя не эмулирует все ограничения systemd.
daemon-reload и зависимости
После изменения unit-файла перечитайте конфигурацию, перезапустите сервис и сразу проверьте свежие записи:
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
sudo systemctl status myapp.service --no-pager -l
sudo journalctl -u myapp.service -b -n 50 --no-pager
daemon-reload не перезапускает приложение, а restart без reload может использовать ранее загруженную конфигурацию.
При ошибке зависимости проверьте её отдельно:
sudo systemctl list-dependencies myapp.service
sudo systemctl show myapp.service -p Requires -p Wants -p After
sudo systemctl status required.service --no-pager -l
sudo journalctl -u required.service -b --no-pager
After задаёт порядок, но само по себе не притягивает другой юнит. Wants создаёт мягкую зависимость, а Requires — более сильную. Не маскируйте проблему задержкой запуска: найдите ресурс, который должен быть готов, и отвечающий за него юнит.
Чек-лист перед повторным запуском
Loadedуказывает на ожидаемый unit-файл.- В журнале найдена первичная ошибка, а не только итог
Failed to start. ExecStartсодержит правильную команду, аргументы и пути.- Исполняемый файл существует, имеет корректный shebang и доступен сервисному пользователю.
UserиGroupсуществуют.WorkingDirectoryсуществует и доступен для прохода.EnvironmentFileчитается и указан без опечаток.- Права выданы минимально необходимым образом, без
chmod 777. - Зависимые юниты активны, а порядок запуска описан осмысленно.
- После правки выполнены
systemd-analyze verify,daemon-reloadиrestart. - После запуска проверены
statusи свежий журнал.