Если вы хоть раз поднимали Laravel-проект в Docker Compose, наверняка сталкивались с ситуацией: контейнер с приложением стартует раньше, чем база данных успевает принять соединения, и миграции падают с ошибкой SQLSTATE[08006] или Connection refused. Перезапустишь — всё работает. На локалке терпимо, но в продакшене — это в падающие деплои.
По умолчанию Docker считает контейнер «живым», если его процесс запущен. Но это не всегда означает, что сервис внутри готов к работе.
Решение — правильно настроенные healthcheck’и и условие depends_on с параметром condition: service_healthy. В этой статье разберём, как это сделать для типичного стека Laravel: PHP-FPM, PostgreSQL, Redis и Nginx.
Почему depends_on без healthcheck не работает
Многие думают, что depends_on: [db] заставит контейнер с приложением ждать, пока база данных будет готова. На самом деле Docker Compose ждёт только запуска контейнера — то есть момента, когда процесс внутри стартовал. Между «процесс запустился» и «база готова принимать запросы» может пройти 5–15 секунд, особенно при первой инициализации.
Чтобы Compose действительно ждал готовности сервиса, нужно:
Определить
healthcheckу зависимого сервиса (БД, Redis и т. д.).В сервисе-потребителе указать
depends_onв расширенной форме сcondition: service_healthy.
Healthcheck для PostgreSQL
В официальный образ Postgres встроена утилита pg_isready — она и есть самая надёжная проверка готовности:
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10sВажный момент — параметр start_period. Он задаёт «грейс-период»: в течение этого времени неуспешные проверки не считаются провалами. Для Postgres это критично, потому что при первом запуске инициализируется PGDATA, и pg_isready временно отвечает «not accepting connections».
Healthcheck для Redis
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
start_period: 5sКоманда redis-cli ping возвращает PONG, когда сервер готов. Если у вас включена авторизация, добавьте -a $REDIS_PASSWORD или используйте переменную окружения REDISCLI_AUTH, чтобы пароль не светился в docker ps.
Healthcheck для PHP-FPM
С PHP-FPM сложнее: в стандартный образ php:8.3-fpm-alpine не входит ни curl, ни wget. Самый универсальный способ — использовать встроенный в PHP-FPM статус-пинг. Включаем его в конфиге пула:
app:
build:
context: .
dockerfile: docker/php/Dockerfile
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000 | grep -q pong"]
interval: 10s
timeout: 3s
retries: 5
start_period: 15sНе забудьте установить fcgi в Dockerfile:
RUN apk add --no-cache fcgiHealthcheck для Nginx
Для Nginx достаточно простой проверки через wget (он есть в alpine-образе):
nginx:
image: nginx:alpine
depends_on:
app:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
ports:
- "8080:80"В конфиге Nginx добавьте отдельный location, который не идёт в PHP:
location = /health {
access_log off;
add_header Content-Type text/plain;
return 200 "ok";
}Полезные флаги
docker compose up --wait — поднимает все сервисы и блокирует консоль до тех пор, пока они не станут healthy (или не упадут). Идеально подходит для CI.
docker compose ps покажет столбец STATUS с пометкой (healthy) или (unhealthy) — удобно для быстрой диагностики на сервере.
Подводные камни
Слишком короткий interval создаёт лишнюю нагрузку. 5–10 секунд — обычно достаточно.
retries × interval определяет максимальное время ожидания. Если БД восстанавливается из бэкапа 2 минуты, а вы поставили retries: 3 и interval: 5s — контейнер пометится как unhealthy раньше, чем база реально упадёт.
Healthcheck должен проверять реальную готовность, а не просто наличие процесса. Например,
pgrep postgresвернёт успех ещё до того, как Postgres примет первое соединение.Не злоупотребляйте
condition: service_healthyв проде на одном узле: если зависимый сервис упадёт и перезапустится, потребитель не «переподпишется» автоматически. Эта механика работает только при старте.
Итог
Несколько строчек YAML экономят часы отладки и убирают целый класс «магических» проблем: миграции, которые иногда падают; тесты, которые иногда красные; деплои, которые иногда обрываются. Если у вас в проекте до сих пор стоит голый depends_on без условий — самое время это починить.
Больше Healthcheck конструкций для docker compose вы можете найти здесь.





























