Когда ваше приложение состоит из нескольких сервисов, например бэкенда, БД и кэша, невольно возникает вопрос — а как гарантировать, что они запустятся в правильно порядке и вообще увидят друг друга? В Docker это решается с помощью depends_on, тут ничего нового. А вот в Podman подход немного другой. Ну кто бы сомневался!
Автор статьи — Роман Шубин, CTO и автор Telegram-канала Bash Days.
В Podman, насколько я знаю, нет аналога depends_on, как в том же Docker Compose. Но есть механизмы, с помощью которых это все успешно заменяется. У меня получилась такая схема:
- pods — общая среда для контейнеров,
- systemd / Quadlet — управление порядком запуска,
- healthcheck + ожидание готовности,
- скрипты ожидания (wait-for-it и т. д.).
Звучит страшно, но кое-что из этого мы уже разбирали в прошлых статьях. Healthcheck и systemd / Quadlet — база, которая необходима для полного понимания процессов.
Простой пример
Поехали ковыряться. Допустим, у нас есть максимально понятный кейс: база должна стартовать первая, потому что клиент зависит от нее:
podman run -d --name db -e POSTGRES_PASSWORD=secret postgres
podman run --rm postgres pg_isready -h db -p 5432
При выполнении второй команды вы увидите:
db:5432 - no response
Это может показаться странным, ведь контейнер db уже запущен. В чем тогда проблема? Запущенный контейнер ≠ сервис готов. Когда вы запускаете PostgreSQL, контейнер стартует быстро, но внутри идет инициализация, создаются файлы, поднимается сервер. Это может занимать продолжительное время.
Соответственно, вторая команда пытается подключиться к базе, но ничего не получается. Порт 5432 еще не активен и не принимает соединения. Podman — это не Kubernetes, который ждет готовности другого контейнера. Если вы не учитываете это явно, то получаете нестабильный запуск.
Кстати, пример выше постоянно будет выдавать no response, даже если контейнер с базой поднялся. Все, как обычно, упирается в сеть.
Создаем сеть:
podman network create mynet
Снова запускаем:
podman run -d --name db --network mynet -e POSTGRES_PASSWORD=secret postgres
И пробуем подключиться:
podman run --rm --network mynet postgres pg_isready -h db -p 5432
Не забываем указать сеть, в которой будут работать контейнеры. У меня это mynet.

Все, добились правды. В первые пять секунд получаем no response, затем все хорошо.
В Podman зависимости между контейнерами — это не только порядок запуска. Сначала нужно обеспечить, чтобы контейнеры вообще «видели» друг друга и только потом решать проблему готовности сервисов. Едем дальше.
Пример посложнее
Что такое pod, я рассказывать не буду. Это базовая база, о которой не написал еще только ленивый. Если коротко, pod’ы делят одну сеть, видят друг друга как localhost и могут использовать общие ресурсы. Внутри pod’ов контейнеры не общаются по имени, они общаются через localhost внутри одного неймспейса. По сути, это концепция Kubernetes.
Создаем pod:
podman pod create --name mypod -p 5432:5432
Здесь создался общий network namespace, порт 5432 прокинут наружу.

Запускаем контейнер внутри нашего нового pod’а:
podman run -d --pod mypod --name db -e POSTGRES_PASSWORD=secret postgres
Проверяем подключение внутри pod’а:
podman run --rm --pod mypod postgres pg_isready -h localhost -p 5432

Магия! Все работает через localhost, никаких DNS, создания сетей и обращений по именам.
Это все прекрасно. Правда, pod решает проблему сети, но не решает проблему готовности. Вот этой командой, например, можно запустить все сразу:
podman run --pod mypod postgres pg_isready -h localhost
Но мы получим ожидаемый результат — no_response:

Ошибочка. База не успела запуститься. Podman решает проблему связности контейнеров через pod, но ответственность за порядок запуска и готовность сервисов остается на вас. Опять никакой магии.
Давайте решать эту проблему. Можно все обернуть в wait-логику:
until pg_isready -h localhost -p 5432; do
echo "Waiting for database..."
sleep 1
done
echo "Database is ready!"
Для Podman это будет выглядеть так:
podman run --rm --pod mypod postgres sh -c 'until pg_isready -h localhost -p 5432; do sleep 1; done; echo ready'
Кишка какая-то получилась, но рабочая. По итогу получаем, что в цикле будет крутиться проверка, а как только база запустится, клиент выдаст ready:

Получился так называемый «ручной depends_on». То есть, костыль. Но поверьте моему опыту, такие костыли постоянно делают, потому что это просто, быстро и очевидно. А самое главное — это работает. Что еще надо?
Надежность запуска в Podman достигается не порядком запуска контейнеров, а проверкой готовности зависимых сервисов.
Нет, так не пойдет. Надо сделать по-взрослому, без условной Bash-кишки и циклов. Давайте посмотрим в сторону systemd и Quadlet.
Можно, конечно, сделать так для app.service:
[Unit]
Description=My App
Requires=db.service
After=db.service
[Container]
Image=postgres
Exec=sh -c '
until pg_isready -h localhost -p 5432; do
echo "waiting for db"
sleep 1
done
echo "DB is ready"
sleep infinity
'
[Install]
WantedBy=multi-user.target
По сути, мы поменяем шило на мыло. Вариант рабочий, но его отбрасываем и смотрим немного в другую сторону. В сторону Notify=healthy. Эта штука будет связывать healthcheck контейнера с systemd. Systemd будет считать сервис «запущенным», только когда контейнер стал healthy.
Как это будет работать
Systemd запускает db, Podman ждет, пока healthcheck станет healthy, и только потом запустится app.service. Вот тут мы практически приблизились к концепту depends_on. Пилим реализацию.
db.container
[Unit]
Description=PostgreSQL container
[Container]
Image=postgres
ContainerName=db
Environment=POSTGRES_PASSWORD=secret
HealthCmd=pg_isready -U postgres
HealthInterval=5s
HealthRetries=5
Notify=healthy
[Install]
WantedBy=multi-user.target
app.container
[Unit]
Description=My App
Requires=db.service
After=db.service
[Service]
Restart=always
[Container]
Image=postgres
Exec=sh -c "echo app started && sleep infinity"
[Install]
WantedBy=multi-user.target
Но опять же нужно быть внимательным с healthcheck, потому что он может быть неточным, слишком простым, не покрывать все кейсы. Лучше иметь двойную логику и добавлять еще wait. На простых проектах это можно опустить, на серьезных — нужно подходить с головой.
Podman дает гибкость, можно решать зависимости вручную, а можно делегировать их systemd через healthcheck.
Почему нельзя просто сделать depends_on, как в Docker? Потому что даже depends_on не ждет готовности. Он следит только за порядком запуска.
Ни Docker, ни Podman, ни systemd не решат проблему готовности за вас. Единственный надежный способ — проверять ее явно.
Решаем проблему управления зависимостями
Так, мы начинали про pod’ы, но может показаться, что как-то плавно скатились в контейнерную рутину. Но это не так. Ключевая мысль тут в том, что pod и зависимости — это разные уровни задачи.
Pod отвечает за общую сеть, совместный неймспейс и удобную связанность контейнеров. Он решает проблему — как эти контейнеры будут между собой взаимодействовать и общаться.
Pod не управляет зависимостями, он не умеет ждать другой контейнер, контролировать порядок запуска, проверять готовность. Поэтому, чтобы объединить контейнеры на уровне systemd, мы создаем еще один юнит.
[Unit]
Description=My Pod
[Pod]
PodName=mypod
PublishPort=5432:5432
[Install]
WantedBy=multi-user.target
И уже к этому pod’у привязываем нужные контейнеры:
[Container]
Image=postgres
ContainerName=db
Pod=mypod
Environment=POSTGRES_PASSWORD=secret
Абстракция на абстракции. Но если вы работаете с Podman, то наверняка с этим регулярно сталкиваетесь.
В Podman зависимости между контейнерами — это комбинация нескольких уровней:
- systemd управляет порядком запуска,
- само приложение отвечает за ожидание зависимостей,
- healthcheck показывает реальное состояние.
Такой подход требует чуть больше явной настройки, но дает полный контроль и предсказуемость в продакшене.