Docker Compose depends_on: контейнер запущен, а PostgreSQL ещё не готов
Вы уже подключаете API к db:5432, но первый запрос после docker compose up иногда падает. Через несколько секунд повтор работает. Смена адреса здесь ничего не даст: Compose создал контейнер базы раньше API, а самому PostgreSQL ещё нужно инициализировать каталог данных и начать принимать соединения.
Что именно гарантирует depends_on
Короткая запись depends_on: [db] задаёт порядок запуска: сначала сервис db, потом зависимый сервис. «Запущен» для контейнера означает, что его основной процесс стартовал. Это ещё не ответ на вопрос, может ли приложение выполнить запрос к базе. Особенно заметна разница при первом запуске, когда PostgreSQL создаёт начальную базу.
Для стартовой зависимости у Compose есть условие service_healthy. Базе задают healthcheck, который периодически выполняется внутри контейнера. Compose дождётся статуса healthy и только затем создаст зависимый сервис. Практический тест для PostgreSQL — pg_isready: его код 0 означает, что сервер принимает подключения. У официального образа при первой инициализации временный сервер слушает только Unix-сокет. Поэтому -h 127.0.0.1 заставляет проверку дождаться TCP-сервера, к которому затем пойдёт соседний контейнер. Правильность пароля и наличие нужных таблиц pg_isready не подтверждает; после старта полезен настоящий запрос от имени приложения.
Минимальный пример с проверочным клиентом
Вместо приложения здесь api-check: он запускает psql и делает SELECT 1. Так виден переход от готовности сервера к реальному соединению. Учебные значения demo держите только в локальном примере; порт базы наружу не открывается.
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: demo
POSTGRES_USER: demo
POSTGRES_PASSWORD: demo
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 2s
timeout: 2s
retries: 15
start_period: 20s
api-check:
image: postgres:16-alpine
depends_on:
db:
condition: service_healthy
environment:
PGPASSWORD: demo
entrypoint: ["psql", "-h", "db", "-U", "demo", "-d", "demo", "-Atqc", "SELECT 1"]
В отдельной учебной папке запустите Compose и дождитесь завершения проверочного клиента:
docker compose up --exit-code-from api-check
api-check стартует после успешного healthcheck и печатает 1. Опция --exit-code-from возвращает код проверки и останавливает сервисы после её завершения. В реальном проекте на месте api-check будет ваше приложение, а строка depends_on останется той же. Двойной доллар в $$POSTGRES_USER нужен для Compose: он сохраняет знак доллара до запуска команды внутри контейнера, где оболочка подставит переменную PostgreSQL.
Параметры interval и timeout задают частоту и время одной попытки, retries — сколько неудач приведут к состоянию unhealthy. start_period даёт базе время на начальную инициализацию: неудачи этого периода не расходуют обычный счётчик попыток. Если контейнер остаётся unhealthy, начните с docker compose ps db и последних строк docker compose logs --tail=50 db. Они отделяют долгую инициализацию от ошибки самой базы; увеличение таймера не исправит, например, неверную конфигурацию PostgreSQL.
По результату pg_isready можно сразу сузить поиск: код 1 означает, что сервер ещё отвергает подключения, код 2 — что он не ответил. Когда healthcheck становится зелёным, запрос SELECT 1 из api-check проверяет уже маршрут db:5432 и пароль учебного пользователя. В своём API проделайте такую же проверку его реальным клиентом базы.
Готовность при старте и связь во время работы
service_healthy решает вопрос первоначального порядка. Если база перезапустится позже, открытые соединения API могут оборваться. HTTP-обработчик или слой доступа к данным должен корректно вернуть ошибку текущей операции и установить новое соединение для следующей; одно условие в Compose не заменяет такого поведения. Если приложению для запуска нужна не просто принимающая соединения база, а уже применённая схема, выделите проверку миграций: pg_isready не знает о ваших таблицах.
Проверяйте два разных момента: в логе старта API должен появиться запуск после перехода db в healthy; при временной недоступности базы следующий пользовательский запрос должен получить понятную ошибку, а после восстановления новый запрос — снова пройти. Так вы видите, что условие Compose решает старт, а код приложения справляется с разрывом во время работы.
Есть полезное разделение диагностики. connection refused только в первые секунды при верном db:5432 ведёт к проверке готовности. Постоянный отказ из контейнера API при работающей базе сначала требует проверить адрес и сеть. Отдельная статья о переменных Compose поможет, если конфигурация показывает один адрес, а старый контейнер использует другой.
Где отработать запуск приложения с базой
В платном курсе «Docker и Docker Compose: контейнеризация приложений» вы настроите связку HTTP-сервиса и PostgreSQL, проверите готовность через healthcheck, сохраните данные в томе и разберёте поломки запуска. В итоговом проекте упакуете приложение с базой и проверите восстановление данных. Откройте программу курса и пробный урок о готовности базы, чтобы пройти следующий шаг на полноценном Compose-проекте.
Если пока трудно отличить порт хоста от внутреннего адреса сервиса, начните с подборки про Docker. Эта статья посвящена моменту, когда адрес уже выбран верно, а базе ещё требуется время на старт.
