Found ordering cycle: циклическая зависимость при загрузке
Сообщение «Found ordering cycle on …» значит, что службы требуют запуска друг после друга по кругу. systemd разрывает цикл, произвольно отбрасывая одну связь, и запуск проходит — но не в том порядке, который вы задумывали. Иногда одна из служб при этом вообще не поднимается.
Что это значит
Цикл почти всегда возникает из смешения двух разных понятий. After= задаёт порядок, Requires= задаёт необходимость. Если добавлять их «на всякий случай» в обе стороны, круг возникает сам.
В журнале загрузки systemd печатает всю цепочку: он перечисляет unit, участвующие в цикле, и говорит, какую связь удаляет. Это и есть готовый ответ, где рвать по-настоящему.
Вероятные причины
По порядку: сверху то, что встречается чаще.
-
Взаимные
After=между двумя службамиКаждая объявила, что должна стартовать после другой. Одновременно это невыполнимо.
-
Цикл через цель
Служба объявила
Before=multi-user.target, а цель тянет службу, которая зависит от первой. Цикл идёт через цель и потому не виден при взгляде на две службы. -
Цикл через
RequiresMountsFor=или автоматические зависимостиsystemd сам добавляет зависимости: службы после локальных файловых систем, точки монтирования после устройств. Собственные связи, направленные против них, замыкают круг.
Диагностика
Команды идут в том порядке, в котором их стоит выполнять: каждая следующая проверяет то, что осталось после предыдущей.
Главная команда: печатает цепочку цикла и то, какую связь systemd отбросил.
journalctl -b --no-pager | grep -iE "ordering cycle|breaking ordering|deleting job"Находит часть проблем с зависимостями без перезагрузки.
systemd-analyze verify myapp.serviceДерево зависимостей: помогает увидеть круг глазами.
systemctl list-dependencies myapp.service --no-pagerРешение
Решение своё для каждой причины. Сначала определите, какая из них ваша, — иначе правки наложатся друг на друга.
After= между двумя службами- Почему происходит
- Каждая объявила, что должна стартовать после другой. Одновременно это невыполнимо.
- Как проверить
-
Найдите цепочку в журнале загрузки.
journalctl -b --no-pager | grep -iE "ordering cycle|breaking ordering" | head -20
- Как исправить
- Оставьте порядок только в одну сторону — там, где он действительно нужен. Вторую связь уберите.
- Почему происходит
- Служба объявила
Before=multi-user.target, а цель тянет службу, которая зависит от первой. Цикл идёт через цель и потому не виден при взгляде на две службы.
- Как проверить
-
Постройте дерево зависимостей в обе стороны.
systemctl list-dependencies myapp.service --no-pager | head -20 systemctl list-dependencies --reverse myapp.service --no-pager | head -20
- Как исправить
-
Уберите
Before=для цели: включение службы в цель и так обеспечивает нужный порядок через механизм [Install].
RequiresMountsFor= или автоматические зависимости- Почему происходит
- systemd сам добавляет зависимости: службы после локальных файловых систем, точки монтирования после устройств. Собственные связи, направленные против них, замыкают круг.
- Как проверить
-
Посмотрите, какие зависимости добавлены автоматически.
systemctl show myapp.service -p After -p Before | tr " " "\n" | head -30
- Как исправить
-
Не объявляйте порядок относительно системных целей вручную. Для каталога на отдельном разделе достаточно
RequiresMountsFor=, остальное systemd достроит сам.
Пример вывода
Две службы объявили порядок друг относительно друга. Пример показательный: он собран на тестовой машине специально для этой страницы, а не взят из чужого журнала.
systemd[1]: Found ordering cycle on backend.service/start
systemd[1]: Found dependency on frontend.service/start
systemd[1]: Found dependency on backend.service/start
systemd[1]: backend.service: Job frontend.service/start deleted to break ordering cycle starting with backend.service/start
systemd[1]: Started backend.service - Backend API.
Последняя строка обманчива: служба запустилась, но зависимость frontend была отброшена, и порядок оказался не тем, что задумывался.
Связанные ошибки
- Dependency failed for … и результат 'dependency' Сообщение Dependency failed for: служба не запускалась, потому что упала её зависимость. Как найти настоящего виновника.
- Unit … not found: unit-файл не найден Сообщение Unit not found при запуске службы: файла нет, не выполнен daemon-reload, опечатка в имени или не указано расширение.
- Connection refused в журнале службы Соединение отклонено: служба не может подключиться к базе, кешу или другому сервису. Порядок запуска, адрес, брандмауэр.
- Замаскированная зависимость мешает загрузке Служба не запускается, потому что замаскирован unit, который она требует. Как найти все замаскированные unit.
- Контейнеры не поднимаются при загрузке сервера Контейнеры с политикой перезапуска не стартуют после перезагрузки: docker не включён, тома на неподмонтированном разделе, своя служба без зависимостей.
- Служба стартует раньше своего сокета При сокет-активации порядок обратный обычному: сокет должен подниматься первым. Как это объявить.
- Цель не достигнута: служба ждёт target, который не наступает Служба не запускается, потому что не достигнута цель из After= или Requires=. Разбор целей multi-user, network-online, graphical.
- A start job is running: загрузка висит на одной службе Загрузка останавливается с обратным отсчётом: служба не укладывается в таймаут. Как найти виновника и не ждать.
Источники
-
systemd.unit(5)
After=, Before=, Requires= и автоматические зависимости по умолчанию. -
Воспроизведено на тестовой машине, systemd 255 (Ubuntu 24.04)
Воспроизведено взаимными After= у двух служб: в журнале видно, какую связь systemd удалил.