Состояние running означает только, что процесс контейнера запущен. База при этом ещё может применять миграции и не принимать соединения. Поэтому sleep 10 иногда работает, а после медленного старта снова ломается.
Рабочий вариант
Добавьте проверку самой базы и зависимость по здоровью:
services:
db:
image: postgres:18-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 12
start_period: 20s
app:
build: .
depends_on:
db:
condition: service_healthy
Проверка должна отвечать на реальный вопрос: «сервис готов обслужить приложение?». Наличие процесса или открытого TCP-порта недостаточно. У HTTP-сервиса лучше проверять лёгкий endpoint /health, который не меняет данные.
Проверка
Запустите docker compose up -d, затем docker compose ps и docker inspect --format '{{json .State.Health}}' <container>. Если healthcheck падает, выполните его команду вручную через docker compose exec.
depends_on управляет стартом, но не заменяет повторные подключения в приложении: сеть может пропасть и позже. Клиент базы всё равно должен иметь ограниченный retry с паузой.
Типичные ошибки
- В образе отсутствует
curl, а проверка молча завершается кодом 127.
- Проверяется
localhost, хотя сервис слушает только другой интерфейс.
- Слишком короткий
start_period помечает нормальный прогрев как ошибку.
- Тяжёлая проверка сама создаёт нагрузку.
Официальные источники