Systemd-сервіс, що не стартує, майже ніколи не загадковий. Виконайте systemctl status <unit>, потім прочитайте останні 50 рядків журналу того юніта через journalctl -u <unit> -n 50 --no-pager — між ними зазвичай прямо названо одну з п’яти причин: кривий шлях, відсутній бінарник, неправильні права, відмова SELinux/AppArmor чи синтаксична помилка юніт-файла. Причина збою — у логу; виправлення нижче — просто пошук шаблонів за ним.
Як побачити, чому впав systemd-сервіс?
Спершу статус, потім журнал:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager status дає стан (inactive (dead), failed (exit-code), activating (auto-restart)) і останні кілька рядків логу. Журнал дає всю історію: stdout, stderr і власні скарги systemd до юніта.
Якщо юніт падав раніше і вам потрібен запис саме того запуску — додайте -b для поточного завантаження чи --since today:
journalctl -u myapp.service -b --no-pager Повний інструментарій — завантаження, пріоритети, живе стеження — у шпаргалці journalctl. Та сама м’язова пам’ять.
Ще одна пара, варта знання:
systemctl list-units --failed
systemctl reset-failed myapp.service Перша перелічує всі червоні юніти на машині. Друга знімає стан «failed» після виправлення — косметика, але вона зупиняє сторінки статусу від фальшивих тривог.
Які найпоширеніші причини?
Після того, як лог назвав симптом, причина майже завжди одна з п’яти:
| Лог каже | Ймовірна причина | Виправлення |
|---|---|---|
status=203/EXEC | Кривий ExecStart= шлях чи відсутній інтерпретатор | Абсолютний шлях, chmod +x, перевірте shebang |
status=203/EXEC на скрипті | Скрипт має CRLF-закінчення рядків чи кривий shebang | dos2unix script.sh, полагодіть перший рядок |
status=1/FAILURE без логів застосунку | Робоча тека чи змінна середовища відсутні | Задайте WorkingDirectory=, додайте Environment= |
Permission denied | Користувач не читає файли чи не б’єдить порт | Полагодьте власність; порти нижче 1024 вимагають AmbientCapabilities=CAP_NET_BIND_SERVICE чи root |
Unit is masked | Хтось виконав systemctl mask | systemctl unmask myapp.service |
| Редагування юніт-файла «нічого не робить» | Демон не перечитано | systemctl daemon-reload |
Родина 203/EXEC варта окремої згадки, бо саме вона з’їдає дні. Systemd не запускає ExecStart= через ваш шелл. Це означає:
# Погано — немає шеллу, ~ ніколи не розгортається, пошуку в PATH немає
ExecStart=~/app/run.sh
# Добре
ExecStart=/opt/app/run.sh І сам скрипт має бути виконуваним і починатися зі справжнього shebang (#!/bin/bash чи #!/usr/bin/env bash). Скрипт, що ідеально працює з вашої термінали, а під systemd падає з 203 — майже завжди один із: не виконуваний, CRLF-закінчення, shebang у нікуди, відносний шлях.
Чому стартує вручну, але не на завантаженні?
Класичний баг порядку. Якщо лог показує відмови одразу після boot, але юніт спокійно стартує, коли ви запускаєте його руками через systemctl start — ваш сервіс програє перегони: тягнеться до мережі, змонтованого диска чи бази даних раніше, ніж вони з’являються.
Виправлення — оголосити залежності замість сподівань:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml After= впорядковує старт; Wants= змушує systemd справді підняти залежність. network-online.target працює лише тоді, коли сервіс очікування мережі ввімкнено у вашій дистрибутиві, тож перевірте systemctl is-enabled NetworkManager-wait-online.service (чи еквівалент systemd-networkd). Охорона ExecStartPre= — дешевий, чесний спосіб впасти голосно з читабельним повідомленням замість стектрейсу.
Другий варіант: сервіс стартує на boot, але миттєво вмирає. Шукайте те, що було в інтерактивному середовищі й немає на boot — відмінності PATH, virtualenv, HOME. Задайте потрібне явно:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app Чому мій сервіс не логує в журнал?
Якщо journalctl -u порожній, перевірте три речі за порядком:
StandardOutput=іStandardError=в юніті — вони мають бутиjournal(дефолт) чиjournal+console. Хтось міг поставитиnull.- Застосунок пише у файл замість stdout. Systemd ловить лише stdout/stderr; файлові логери обходять журнал повністю. Або направте застосунок на stdout, або читайте файл.
- Ліміти зберігання викинули старі рядки:
journalctl --disk-usage, іSystemMaxUse=у/etc/systemd/journald.conf, якщо журнал давиться.
А для дебагу самого старту нема нічого кращого за кинути шелл у boot-контекст:
[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1' Або, для справжніх загадок, виконайте точну команду ExecStart= від імені юзера юніта в шеллі — більшість відмінностей середовища спливають у перші десять секунд.
Як зробити автоперезапуск після падіння?
Дефолтна політика — Restart=no: впалий сервіс лишається мертвим, і ви дізнаєтеся від користувача. Виправте для кожного сервісу:
[Service]
Restart=on-failure
RestartSec=5 | Налаштування | Перезапускає коли |
|---|---|
no (дефолт) | Ніколи |
on-failure | Ненульовий вихід, сигнал, таймаут |
always | Будь-який вихід, навіть чистий |
on-watchdog | Лише таймаут watchdog |
Спарте Restart=on-failure з охороною темпу стартів, щоб цикл падінь не молотив машину: StartLimitIntervalSec= і StartLimitBurst= у секції [Unit]. П’ять падінь за шістдесят секунд мають пінгувати людину, а не крутити CPU.
Якщо ви збираєте це як заплановану задачу, а не демона — спершу зважте cron vs systemd timer: таймери дають журналювання й упорядкування залежностей задарма, саме те, до чого ця стаття тягнеться весь час.
Чекліст на 60 секунд
systemctl status <unit>— прочитайте стан і останні рядки.journalctl -u <unit> -n 50 --no-pager— знайдіть справжню помилку.203/EXEC? Полагодьте шлях, shebang, права.Permission denied? Полагодьте користувача і власність файлів.- Падає лише на boot? Додайте
After=/Wants=network-online.targetта охоронуExecStartPre=. - Змінили юніт-файл?
systemctl daemon-reload && systemctl restart <unit>. - Додайте
Restart=on-failure, щоб наступне падіння саме назвалося, а не ховалося.
Більшість моментів «systemd складна» згортаються до фрази: помилка весь час була в журналі. Читайте його до редагування юніт-файла, а не після.
— mrsaynothing
— mrsaynothing
Польові нотатки про ШІ, Linux і self-hosting.
Обговорити пост на dev.to dev.to ↗
Наступний гайд — на email
Один лист на пост. Полагодили — і далі.
що це таке?Квантування GGUF: який рівень обрати?
Читається добре? Таке я будую за гроші. найміть мене