HEALTHCHECK в Docker — это встроенный механизм, который периодически запускает команду внутри контейнера и оценивает его состояние: healthy, starting или unhealthy. При правильно заданной проверке вы получаете предсказуемый старт зависимостей, аккуратные rolling‑обновления, а мониторинг получает корректные сигналы о деградации. Но по умолчанию restart‑политики Docker не привязаны к состоянию health. В этой статье разберём, как писать проверку так, чтобы она была быстрой и надёжной, какие бывают шаблоны для HTTP/TCP/CLI‑проверок, и как интегрировать результат с restart policy и процессами деплоя на VDS.
Как работает HEALTHCHECK в Docker
Механика проста: вы объявляете команду проверки и параметры периодичности в Dockerfile или docker-compose. Демон запускает её по расписанию. Если команда возвращает код выхода 0 — контейнер считается healthy. Любой ненулевой код трактуется как unhealthy. Пока контейнер не прошёл ни одной успешной проверки, статус будет starting (на него влияет start-period).
Ключевые параметры:
--interval— как часто запускать проверку (например, 30s).--timeout— сколько ждать завершения проверки (например, 3s).--retries— сколько подряд неуспешных запусков нужно, чтобы пометить как unhealthy.--start-period— льготный период «прогрева», в который фейлы не учитываются.
Посмотреть статус можно командами:
docker ps
docker inspect --format '{{json .State.Health}}' <container>
docker inspect <container> | grep -A5 Health
Главные принципы грамотного healthcheck
- Проверка должна быть быстрой (миллисекунды — единицы секунд), иначе она сама станет проблемой.
- Проверяйте «готовность» сервиса, а не «живость» процесса. Открытый порт не значит, что приложение готово обслуживать запросы.
- Изолируйте зависимости проверки. В идеале команда опирается только на то, что уже есть в вашем образе.
- Локальность прежде всего: используйте
127.0.0.1илиlocalhost, избегайте DNS внешних доменов и внешних сетей (если нужна надёжность сети, поможет настройка Docker firewall: iptables и nftables). - Подбирайте параметры под реальное поведение приложения: учтите прогрев JIT/кэшей/миграций БД.
- Логируйте кратко. Вывод проверки попадает в метаданные контейнера и логи; длинные сообщения будут мешать.
Цель healthcheck — повысить стабильность, а не «наказать» контейнер. Избыточно строгая проверка срежет доступность не хуже реального падения.

Шаблоны проверок: HTTP, TCP, CLI
HTTP‑проверка сервиса
Базовый случай для веб‑приложений: запрос к локальному эндпоинту, который быстро отвечает 200 OK. Не используйте тяжёлые маршруты, рендер и работу с БД, если можно иметь облегчённую точку /health или /ready.
# Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 CMD curl -fsS http://127.0.0.1:8080/health || exit 1
Если в образе нет curl, допустим wget -q --spider, либо перенос проверки в скрипт:
# Dockerfile
COPY health.sh /usr/local/bin/health.sh
RUN chmod +x /usr/local/bin/health.sh
HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 CMD /usr/local/bin/health.sh
# /usr/local/bin/health.sh
#!/bin/sh
URL="${HEALTHCHECK_URL:-http://127.0.0.1:8080/health}"
exec wget -q --spider "$URL"
TCP‑проверка порта
Когда у приложения нет HTTP‑эндпоинта (SMTP, Redis, кастомные TCP‑сервисы), можно проверять установку TCP‑соединения.
# BusyBox/Alpine: netcat-openbsd или netcat
HEALTHCHECK --interval=20s --timeout=2s --retries=5 CMD nc -z -w 2 127.0.0.1 6379
Альтернатива без утилит — bash c /dev/tcp:
HEALTHCHECK --interval=20s --timeout=2s --retries=5 CMD bash -c 'echo >/dev/tcp/127.0.0.1/6379'
CLI‑проверка «готовности» СУБД
Для PostgreSQL и MySQL есть быстрые встроенные команды готовности:
# PostgreSQL
HEALTHCHECK --interval=10s --timeout=2s --retries=5 CMD pg_isready -U "$POSTGRES_USER" -h 127.0.0.1 -p 5432
# MySQL/MariaDB
HEALTHCHECK --interval=10s --timeout=2s --retries=5 CMD mysqladmin ping -h 127.0.0.1 -u "$MYSQL_USER" --silent
Если вы храните пароль в окружении, используйте заранее подготовленный .my.cnf или переменные окружения клиента БД, чтобы не светить секреты в аргументах.
Шаблон для приложений с зависимостями
Иногда приложение «готово» только если оно успешно подсоединяется к БД/кэшу. Дешёвый вариант — проверка своей же внутренней readiness‑логики через облегчённый HTTP‑эндпоинт. Если такого нет — небольшая оболочка‑скрипт:
# /usr/local/bin/app-health.sh
#!/bin/sh
# 1) порт
nc -z -w 1 127.0.0.1 8080 || exit 1
# 2) быстрая проверка зависимостей
pg_isready -U "$POSTGRES_USER" -h "$DB_HOST" -p 5432 || exit 1
# 3) опционально: проверка очереди/кэша
redis-cli -h "$REDIS_HOST" PING | grep -q PONG || exit 1
exit 0
Минимизируйте число внешних вызовов и избегайте DNS, чтобы сама проверка не становилась точкой отказа.
Параметры: как подобрать интервалы и таймауты
- HTTP‑приложение:
interval=15–30s,timeout=2–3s,retries=3,start-period=20–60s. - БД и брокеры (быстрая готовность):
interval=10–20s,timeout=1–2s,retries=5,start-periodпо старту демона. - Тяжёлый прогрев (миграции/кеши): увеличьте
start-periodдо реального worst‑case; не завышайтеtimeout, лучше повышайтеretries.
Не пытайтесь «лечить» медленную проверку большим timeout. Лучше сделайте проверку проще и быстрее.
HEALTHCHECK и restart policy: что работает, а что нет
Важно понимать: в Docker здоровье контейнера не вызывает его автоматический перезапуск. Restart policy (no, on-failure, always, unless-stopped) реагирует только на выход основного процесса. Поэтому «контейнер unhealthy» и «контейнер перезапускается» — это разные события по умолчанию.
Рабочие связки
- Порядок запуска в Compose через
depends_onиservice_healthy. Вы можете заставить сервис B стартовать только после того, как сервис A стал healthy. Это решает проблему гонок по старту зависимостей. - Грейс‑переключение при обновлениях. В Docker Swarm и некоторых стратегиях деплоя обновления ждут статуса healthy, прежде чем выключать старый экземпляр. Корректный healthcheck — ключ к нулевому даунтайму.
- Авто‑рестарт при деградации через внешний «сторож». Если вам нужен рестарт при unhealthy, добавьте процесс, который подписывается на события Docker и перезапускает контейнеры при смене статуса. Это может быть отдельный контейнер‑watcher или системный юнит на хосте.
Пример: depends_on с ожиданием service_healthy
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -h 127.0.0.1 -p 5432"]
interval: 10s
timeout: 2s
retries: 5
start_period: 20s
app:
build: .
depends_on:
db:
condition: service_healthy
restart: unless-stopped
Такой подход исключает преждевременный старт app, когда db ещё в starting или unhealthy.
Пример «сторожа», который перезапускает unhealthy
Минимальный скрипт на хосте, слушающий события Docker. Он безопаснее, чем попытки «самоубийства» внутри контейнера.
# /usr/local/bin/docker-autoheal.sh
#!/bin/sh
# Требуется доступ к сокету Docker: /var/run/docker.sock
docker events --format '{{json .}}' | while read -r line; do
echo "$line" | grep -q '"status":"health_status"' || continue
id=$(echo "$line" | sed -n 's/.*"id":"\([a-f0-9]\{12\}\)".*/\1/p')
status=$(echo "$line" | sed -n 's/.*health_status: \([a-z]*\).*/\1/p')
[ "$status" = "unhealthy" ] || continue
name=$(docker inspect --format '{{.Name}}' "$id" | sed 's#^/##')
echo "autoheal: restarting $name ($id) due to unhealthy"
docker restart "$id" >/dev/null 2>&1
done
Запускайте его как systemd‑сервис с Restart=always или как отдельный контейнер с доступом к сокету. Политика restart целевого приложения должна быть настроена так, чтобы рестарт допускался (unless-stopped или always).
Почему не стоит «убивать» процесс из healthcheck
Иногда советуют в проверке делать kill 1 при ошибке, чтобы сработала restart policy. Это превращает healthcheck в механизм рестартов, ломает диагностику и может попасть в цикл рестартов при флапе. Лучше отделяйте сигнализацию о здоровье и решение об автоматическом перезапуске (через сторож/оркестратор).
Практичные рецепты для популярных стеков
Nginx
Если есть stub_status или лёгкий локальный upstream, проверяйте его. Без него — хотя бы HEAD на корень.
HEALTHCHECK --interval=30s --timeout=2s --retries=3 CMD wget -q --spider http://127.0.0.1:80/
PHP‑FPM
Идеально — включить ping.path в php-fpm.conf и дергать /ping через локальный Nginx. Без Nginx проверяйте, что мастер‑процесс жив и порт слушается.
HEALTHCHECK --interval=20s --timeout=2s --retries=5 CMD sh -c 'pidof php-fpm >/dev/null && nc -z 127.0.0.1 9000'
Node.js/Go‑сервисы
Добавьте эндпоинт /health, где приложение без БД‑логики отвечает 200 OK, и проверяйте его.
HEALTHCHECK --interval=20s --timeout=2s --retries=3 CMD curl -fsS http://127.0.0.1:3000/health || exit 1
Redis/Key‑Value
Быстрый PING безопаснее, чем TCP‑handshake, потому что проверяет обработку команд.
HEALTHCHECK --interval=10s --timeout=2s --retries=5 CMD sh -c 'echo PING | nc 127.0.0.1 6379 | grep -q PONG'

Compose‑опции healthcheck
В docker-compose можно описывать проверку прямо в сервисе:
services:
web:
image: myorg/web:latest
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/health || exit 1"]
interval: 30s
timeout: 3s
retries: 3
start_period: 20s
restart: unless-stopped
Если вы наследуете образ, в котором задан неподходящий healthcheck, его можно отключить:
# Dockerfile
HEALTHCHECK NONE
Наблюдаемость: как мониторить здоровье
- docker ps показывает колонку STATUS со значением (healthy|unhealthy).
- docker inspect хранит историю прогонов и последние логи проверки в
.State.Health. - docker events даёт поток событий
health_status— подключайте алерты и авто‑действия. Для хранения метрик и событий посмотрите материал «VictoriaMetrics на одном VDS» — подходит для небольших инсталляций: VictoriaMetrics на одном VDS. - Логи: не зашумляйте. Выводите только то, что поможет понять причину.
Анти‑паттерны и типичные ошибки
- Слишком тяжёлая проверка. Для HTTP‑пинга не рендерьте страницы и не ходите в БД без нужды.
- Сетевые зависимости наружу. HEALTHCHECK не должен зависеть от доступности внешних сервисов и DNS.
- Неверные интервалы. Большой
timeout+ маленькийintervalможет приводить к конкурирующим запускам или ненужной нагрузке. - Отсутствие
start-period. Прогрев кэшей и JIT ломает первые проверки — закладывайте реальное время старта. - Скрытые секреты в аргументах. Командная строка попадает в метаданные. Используйте переменные окружения клиентов или конфиг‑файлы.
- Игнорирование статуса «starting». В связках с
depends_onучитывайте, что некоторое время сервис ещё не готов. - Попытка привязать restart policy напрямую к health. Docker этого не делает. Нужен внешний сторож или оркестратор.
Отладка healthcheck
- Запускайте команду проверки вручную через
docker exec, чтобы увидеть реальные ошибки. - Временно снизьте
intervalиretriesдля ускоренной диагностики. - Проверьте лимиты контейнера (CPU/IO). При сильной нагрузке проверка может не успевать за
timeout. - Смотрите
.State.Health.Logчерезdocker inspect— там последние N запусков с выводом.
Связка с обновлениями и развёртыванием
Корректный healthcheck — фундамент для обновлений без простоя: новый экземпляр считается готовым только после успешных проверок, затем можно трафик переключать или останавливать старый. В Swarm обновлятор также учитывает здоровье задач при поэтапном развёртывании. В самостоятельных деплоях на одном хосте этот же эффект достигается за счёт depends_on: service_healthy и сторожа, который не допустит «зависших» unhealthy‑контейнеров. Про безопасность окружения не забывайте: для плотной изоляции полезен подход с gVisor/Firecracker — см. изоляция контейнеров с gVisor и Firecracker.
Чек‑лист
- Есть лёгкий и быстрый эндпоинт или CLI‑проверка, не зависящая от внешних сетей.
- Подобраны
interval,timeout,retries,start-periodпод реальный прогрев и SLA. - В Compose зависимые сервисы ждут
service_healthy. - Настроен мониторинг событий
health_status. - Решено, нужен ли авто‑рестарт при unhealthy и чем он обеспечивается (сторож/оркестратор).
- Отключены неподходящие унаследованные проверки (
HEALTHCHECK NONE).
Итоги
HEALTHCHECK — это дешёвый и эффективный способ повысить стабильность контейнеров: он фиксирует «готовность к работе» вместо простого факта запущенного процесса. Грамотно подобранные параметры и лёгкие шаблоны проверок позволяют связать здоровье с порядком запуска сервисов и сценариями обновления. Для автоматических перезапусков при деградации добавляйте небольшой сторож, а сами проверки держите максимально быстрыми и локальными. Так вы получите предсказуемый старт, корректный мониторинг и устойчивое поведение restart policy в реальных инцидентах.


