Короткий ответ

До правки unit-файла снимите четыре артефакта: systemctl status, systemctl cat, systemctl list-dependencies --failed, journalctl -u и при dumpedcoredumpctl info. Код 203/EXEC почти всегда «бинарника нет или нет +x», 226/NAMESPACE — PrivateTmp/ProtectSystem, SEGV — баг или библиотека, а не «надо Restart=always». Vendor-файл в /lib/systemd/system не редактируют: только drop-in в /etc/systemd/system/<unit>.d/.

Пример: хост host.example (10.0.20.10), пользователь admin. Подставьте имя своего unit вместо app.service.

Симптомы и как отличить

Сервис «не запускается», если:

  • systemctl is-active app.service даёт failed или inactive;
  • Job for app.service failed because the control process exited with error code;
  • после reboot systemctl --failed содержит unit;
  • таймер зелёный, а service красный — это не cron, это тот же systemd.

Отличия:

НаблюдениеНе этот кейсСтатья
Вся машина в emergencyзагрузкаСервер не загружается
Unit dead, таймер не стреляетtimerНе выполняется systemd timer
Exec стартует и пишет Permission deniedправа/ACL/AppArmorPermission denied
start висит минутами, диск 100%ENOSPCЗакончился диск
Служба жила и внезапно SIGKILLOOMOOM Killer

Возможные причины

  1. ExecStart= указывает на путь, которого нет после apt upgrade (Python venv, /opt/app/bin не смонтирован).
  2. Unit ждёт After=network-online.target / Requires=postgresql.service, зависимость failed.
  3. Drop-in с опечаткой: два ExecStart= без ExecStart= пустой строки — systemd не склеивает так, как кажется.
  4. User=app не существует, домашний каталог не создан, ProtectHome=true закрывает сокет.
  5. AppArmor profile deny, namespace (PrivateDevices, NoNewPrivileges) — код 226.
  6. Бинарь падает с SIGSEGV; без coredumpctl это выглядит как «systemd его убивает».
  7. Диск или inode кончились — запись PIDFile/RuntimeDirectory невозможна.

Диагностика

Все команды от admin с sudo на host.example.

1. Состояние и свойства, не файл с форума

systemctl status app.service --no-pager -l
systemctl show app.service -p FragmentPath,DropInPaths,ActiveState,SubState,Result,ExecMainStatus,ExecMainCode,NRestarts
systemctl cat app.service

FragmentPath скажет, откуда unit: /lib (пакет) или /etc (ваш). DropInPaths — что реально наложено. Если cat показывает не то, что вы правили в nano, вы правили копию не того имени (app.service vs app@.service).

2. Зависимости

systemctl list-dependencies app.service --all
systemctl list-dependencies app.service --reverse
systemctl list-units --failed --no-pager

Красный Requires= тянет вниз весь граф. Wants= не должен валить сервис; если валит — у вас скорее Requires или BindsTo.

Для сокетов Ubuntu 24.04: ssh.socket активирует ssh.service по запросу. Не путайте inactive (dead) у service до первого коннекта с поломкой.

3. Журнал именно этого unit

journalctl -u app.service -b --no-pager
journalctl -u app.service -o verbose --since 'today' | head

Ищите status=203/EXEC, Executable not found, Permission denied, Failed to set up mount namespacing. Параллельно диск:

df -h / /var /tmp
df -i / /var

4. Coredump до теории «надо ulimit»

coredumpctl list app.service
coredumpctl info $(coredumpctl list -1 --no-legend | awk '{print $2}')

Если Storage=none в /etc/systemd/coredump.conf, дампов нет — это настройка, не доказательство, что падения не было. Result=core-dump в systemctl show достаточный сигнал не крутить Restart= в цикле.

5. Ручной запуск тем же User и окружением

systemctl show app.service -p User,Group,Environment,EnvironmentFiles,WorkingDirectory,ExecStart
sudo -u app -H /usr/bin/app --config /etc/app/app.yaml

Если вручную работает, а под systemd нет — смотрите ProtectSystem, ReadWritePaths, PrivateTmp, AppArmor:

aa-status
journalctl -t apparmor --since '1 hour ago' --no-pager

Решение

Сценарий A. 203/EXEC

Проверьте путь и shebang:

ls -l /usr/local/bin/app
head -n 1 /usr/local/bin/app
namei -l /usr/local/bin/app

Интерпретатор из shebang (/usr/bin/python3) должен существовать. После правки:

sudo systemctl daemon-reload
sudo systemctl start app.service

Сценарий B. Зависимость не встала

Поднимите нижний unit, не верхний. Пример: Requires=network-online.target при сломанном systemd-networkd-wait-online держит приложение вечно.

systemctl status systemd-networkd-wait-online.service --no-pager
systemctl show app.service -p After,Requires,Wants

Ослабьте через drop-in, не вырезая сеть целиком:

sudo systemctl edit app.service
[Unit]
Requires=
Wants=network-online.target
After=network-online.target

Пустой Requires= сбрасывает vendor-значение в override — документируйте это в заявке.

Сценарий C. Namespace / Protect*

Временно (на диагностику, не навсегда) добавьте drop-in с ProtectSystem=off только чтобы подтвердить гипотезу, затем верните защиту и точечно ReadWritePaths=/var/lib/app.

Сценарий D. SEGV

Сохраните coredumpctl dump -o /var/tmp/app.core. Обновление пакета или откат apt-get install app=предыдущая — после чтения changelog. Не ставьте ulimit -c unlimited в ExecStartPre «чтобы зажило».

Сценарий E. Vendor unit правили руками

dpkg -S $(systemctl show -p FragmentPath --value app.service)
apt-get install --reinstall <пакет>

Ваши изменения перенесите в /etc/systemd/system/app.service.d/override.conf.

Как проверить, что проблема устранена

systemctl reset-failed app.service
systemctl start app.service
systemctl is-active app.service
systemctl show app.service -p NRestarts,ActiveEnterTimestamp
journalctl -u app.service -n 30 --no-pager
ss -tlnp | grep app

NRestarts=0 после часа под нагрузкой важнее зелёного start. Если это сокет-активируемый сервис — проверка клиентским коннектом, не только is-active.

Перезагрузка контрольная:

sudo reboot
ssh admin@10.0.20.10 'systemctl is-active app.service'

Если не помогло

  • Unit активен, приложение не отвечает: это уже сеть, bind на 127.0.0.1, UFW.
  • Падение через 90 секунд: TimeoutStartSec, скрипт ждёт сеть/DNS — DNS.
  • Failed to create cgroup: лимиты контейнера/Proxmox, не systemd на хосте.
  • Логи оборваны vacuum: journald занял диск.

Профилактика

  • Только drop-in в /etc, vendor в /lib не трогать.
  • Restart=on-failure с RestartSec= и потолком StartLimitBurst, не бесконечный цикл.
  • Мониторинг systemctl is-failed и coredump, не только TCP-порт.
  • После apt upgradesystemctl cat на кастомные сервисы.

FAQ

Чем systemctl edit лучше nano в /lib?

edit создаёт override, переживает обновление пакета. Правка /lib всплывёт при следующем apt.

Нужно ли daemon-reload после изменения бинарника?

Нет, только после изменения unit/drop-in. Бинарь подхватится на следующем start.

Почему enable есть, а после reboot сервис dead?

WantedBy не тот target, или ConditionPathExists ложен, или сокет-активация. Смотрите systemctl show -p UnitFileState,ActiveState,ConditionResult.

Можно ли KillMode=process, чтобы дети жили?

Только если вы понимаете orphan-процессы. Для обычного демона оставляйте control-group.

systemd пишет success, а код приложения ненулевой?

Type=simple считает start успешным в момент fork. Для уведомляющих сервисов нужен Type=notify и sd_notify.