Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

Systemd после reboot: network-online.target, Restart= и отладка через journalctl

Если systemd-сервис работает при ручном старте, но «не поднимается» после перезагрузки, обычно виноваты гонки за сетью/DNS или рестарт-циклы. Ниже — практичный разбор network-online.target, After/Wants/Requires, Restart=, лимитов StartLimit* и отладки через journalctl -u.
Systemd после reboot: network-online.target, Restart= и отладка через journalctl

Классическая ситуация: вы делаете systemctl start myapp — всё отлично. Но после reboot сервис либо не стартует, либо стартует и тут же уходит в failed, либо попадает в быстрый рестарт-цикл. В большинстве случаев это не «магия systemd», а комбинация раннего старта и агрессивной политики перезапуска.

Важно помнить: «после загрузки системы» — это не одна точка времени. Юниты и цели (targets) запускаются параллельно, а значит сервис может оказаться в фазе, где ещё не готовы сеть, DNS, монтирования или внешние зависимости.

Почему сервис «работает вручную», но ломается после reboot

Когда вы запускаете сервис вручную, вы обычно делаете это уже после того, как система «успокоилась»: интерфейсы получили адрес, резолвер настроен, диски примонтированы, секреты разложены. При старте на boot сервис может оказаться раньше этих событий.

Типовые причины:

  • Сервис стартует слишком рано: ещё нет IP по DHCP, маршрута по умолчанию, DNS, поднятого VPN, доступного внешнего API.
  • Юнит падает на старте и тут же перезапускается — пока не выбивает лимит StartLimit, после чего systemd перестаёт пытаться.

Быстрый чек-лист диагностики (до правок)

1) Посмотреть статус и понять симптом

systemctl status myapp.service

В статусе смотрим код выхода процесса, причину остановки, счётчик рестартов и сообщения про лимит запуска (StartLimit).

2) Логи конкретного юнита через journalctl

journalctl -u myapp.service -b

Ключ -b показывает логи текущей загрузки — это то, что нужно для «после reboot не взлетело».

Полезные варианты:

journalctl -u myapp.service -b -n 200
journalctl -u myapp.service -b -p warning
journalctl -u myapp.service --since "10 min ago"

Если сервис «мигнул» (успел стартовать и упасть), увеличьте количество строк и ищите первое исключение или первичную ошибку: temporary failure in name resolution, connection refused, no route to host, таймауты.

3) Понять, кто и когда запускает юнит

systemctl cat myapp.service
systemctl show myapp.service -p WantedBy -p After -p Wants -p Requires

systemctl cat показывает основной файл и drop-in overrides. systemctl show удобно читать как «плоскую» сводку зависимостей.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

network-online.target: когда он нужен и почему After=network.target часто не спасает

Частая ошибка — поставить After=network.target и ждать, что «сеть готова». На деле network.target означает лишь, что сетевой стек и сетевой менеджер запущены. Интерфейсы могут ещё не получить адрес, маршрутизация может быть не готова, а DNS — не работать.

Если приложение на старте должно сразу подключиться к удалённой БД, очереди, API или сервис-дискавери, обычно нужен network-online.target.

network-online.target — это ожидание «сеть онлайн» в терминах вашего сетевого менеджера. systemd сам по себе не делает сеть онлайн: он лишь может подождать, если включён соответствующий wait-online юнит.

Что должно быть включено, чтобы ожидание работало

Зависит от того, чем управляется сеть:

  • systemd-networkd: чаще всего systemd-networkd-wait-online.service;
  • NetworkManager: NetworkManager-wait-online.service;
  • прочие решения могут иметь свои механизмы ожидания.
systemctl status systemd-networkd-wait-online.service
systemctl status NetworkManager-wait-online.service

Практический минимум для сетевого сервиса

Универсальный базовый вариант: стартовать после «онлайна» и мягко подтягивать цель.

[Unit]
Description=MyApp
After=network-online.target
Wants=network-online.target
  • After= задаёт порядок.
  • Wants= просит systemd попытаться активировать зависимость, но не превращает её в «жёсткое домино».

Схема порядка загрузки systemd и зависимостей сервиса от network-online.target

After=, Wants=, Requires=: как не устроить «загрузочный домино-эффект»

After= — только порядок, не запуск

After=postgresql.service означает: «мой сервис стартует после попытки запуска PostgreSQL». Но само по себе это не заставляет systemd запускать PostgreSQL.

Wants= — мягкая зависимость

Wants=postgresql.service означает: «когда запускают меня — попробуйте запустить и PostgreSQL». Если PostgreSQL не поднялся, мой сервис всё равно может попытаться стартовать (и уже сам решить, что делать).

Requires= — жёсткая зависимость

Requires=postgresql.service означает: «без PostgreSQL меня не должно быть». Если требуемый юнит упал — мой тоже остановится. Это удобно для обязательных локальных компонентов, но опасно для внешних/нестабильных зависимостей (сеть, удалённая БД, VPN): можно получить цепные остановки и долгие таймауты при загрузке.

Практическое правило

  • Для сети чаще используйте After=network-online.target + Wants=network-online.target.
  • Для локальных обязательных демонов (локальная БД, локальный агент) — рассмотрите Requires=, но только понимая последствия.

Restart= и рестарт-циклы: самовосстановление без «само-DoS»

Если сервис падает из-за временной неготовности зависимостей (сеть/DNS/БД), перезапуск — нормальная стратегия. Но Restart=always без паузы и без контроля частоты быстро превращается в флаппинг: нагрузка, раздувание журнала и блокировка по лимитам запуска.

Выбор Restart= по смыслу

  • Restart=no — не перезапускать.
  • Restart=on-failure — перезапускать при ошибке (часто лучший дефолт для продакшена).
  • Restart=always — перезапускать даже при штатном выходе (уместно для воркеров, которые должны жить постоянно, но требует дисциплины).

RestartSec= — обязательная пауза

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

Пауза даёт время DHCP/DNS/маршрутам и снижает вероятность «выжечь» лимиты.

StartLimitBurst и StartLimitIntervalSec

Если сервис слишком часто падает и перезапускается, systemd перестаёт запускать его и пишет что-то вроде start request repeated too quickly.

Часто параметры задают в [Unit]:

[Unit]
StartLimitIntervalSec=60
StartLimitBurst=10
  • Если зависимость может подняться с задержкой, лучше делать более редкие попытки (увеличить RestartSec) и умеренно поднять лимиты.
  • Если сервис падает из-за конфигурационной ошибки, лимиты лечить не нужно: исправляйте первопричину по журналу.

Паттерн «сеть/БД не готовы»: ожидание vs ретраи

Здесь две разные стратегии:

  • Ожидание на уровне systemd: цели вроде network-online.target, зависимости на локальные сервисы, зависимости на монтирование.
  • Ретраи на уровне приложения: приложение само повторяет подключение к БД/очереди с таймаутами и бэкоффом.

Хорошая практика: systemd обеспечивает правильный порядок и перезапуск, а приложение умеет переживать краткие провалы, не падая мгновенно.

ExecStartPre: короткая проверка перед стартом

Иногда полезно добавить быстрый pre-check, который явно покажет проблему в журнале и не даст запускаться «в пустоту». Главное — не превращать его в бесконечное ожидание.

[Service]
ExecStartPre=/usr/bin/getent hosts db.service.local
Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Как быстро находить первопричину через systemctl status

systemctl status полезен как «рамка»: кто запустил, что пытались, сколько раз перезапускали. Но первопричина почти всегда в journalctl -u.

Типовые строки:

  • Main process exited, code=exited, status=1/FAILURE — приложение вернуло ошибку (ищите конкретное сообщение в журнале выше).
  • ExecStart=... (code=exited, status=203/EXEC) — systemd не смог выполнить файл (путь, права, отсутствует бинарник, неверный ExecStart).
  • Start request repeated too quickly — сработали лимиты запуска; смотрите частоту падений, RestartSec, StartLimitBurst, StartLimitIntervalSec.

Связка команд «после reboot не взлетело»

systemctl status myapp.service
journalctl -u myapp.service -b -n 200
systemctl show myapp.service -p ActiveState -p SubState -p ExecMainStatus -p NRestarts

Пример юнита: нужна сеть и адекватное самовосстановление

Шаблон без «вечных ожиданий», но с корректной интеграцией в загрузку и сдержанными рестартами:

[Unit]
Description=MyApp API service
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=120
StartLimitBurst=12

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yml
Restart=on-failure
RestartSec=10s
TimeoutStartSec=30s
TimeoutStopSec=30s

[Install]
WantedBy=multi-user.target
  • WantedBy=multi-user.target — стандартная «серверная» фаза загрузки.
  • After + Wants на network-online.target — частый базовый минимум для сетевого демона.
  • Restart=on-failure и RestartSec — не дёргаем сервис десятки раз в секунду.
  • StartLimit* — защита от бесконечного флаппинга.

Пример диагностики: вывод systemctl status и journalctl для сервиса, который падает после перезагрузки

Включение автозапуска и проверка «как после reboot»

После правок:

systemctl daemon-reload
systemctl enable myapp.service
systemctl restart myapp.service

Проверить, что enable создал связи:

systemctl is-enabled myapp.service
systemctl list-dependencies --reverse myapp.service

Дальше лучший тест — реальная перезагрузка и проверка лога именно этой загрузки:

systemctl status myapp.service
journalctl -u myapp.service -b

Частые причины, которые маскируются под «проблемы сети»

  • DNS ещё не готов: IP есть, но резолвер не работает. Симптом: temporary failure in name resolution.
  • Маршруты или firewall: интерфейс поднят, но до цели не дойти. Симптомы: no route to host, таймауты.
  • Нет нужных файлов/секретов: конфиг лежит на примонтированном томе. Тогда смотрите RequiresMountsFor= и зависимости монтирования, а не только network-online.target.
  • Права и пользователь: вручную запускали от root, а в юните указан User=. Ошибка будет в журнале.

Мини-руководство по здравому смыслу для зависимостей systemd

Если сервис может стартовать без сети и подождать внутри себя, не перекладывайте бизнес-логику ожидания на systemd. Пусть systemd отвечает за порядок и перезапуск, а приложение — за ретраи и таймауты.

  • Используйте network-online.target только там, где реально нужен «коннект сразу на старте».
  • Не злоупотребляйте Requires= для внешних зависимостей.
  • Ставьте Restart=on-failure и осмысленный RestartSec.
  • Контролируйте флаппинг через StartLimitBurst и StartLimitIntervalSec, но не лечите ими ошибки конфигурации.
  • Всегда начинайте отладку с systemctl status и journalctl -u myapp.service -b.

Шпаргалка команд

systemctl status myapp.service
journalctl -u myapp.service -b -n 200
journalctl -u myapp.service -b -p err
systemctl cat myapp.service
systemctl show myapp.service -p After -p Wants -p Requires -p NRestarts
systemctl daemon-reload
systemctl enable myapp.service
systemctl restart myapp.service

Полезная перелинковка по теме надёжности

Если ваш сервис зависит от PostgreSQL, часто проблема «после reboot» на деле оказывается перегрузкой соединений или долгим прогревом базы. В таком случае пригодятся:

И отдельно: если вы отлаживаете подобное на отдельной машине (стенд/прод с разными профилями сети и DNS), удобнее держать сервисы в изолированной среде на VDS, чтобы воспроизводить проблему «как после reboot» без влияния соседей по хосту.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...