Docker Health Check
A Dockerfile instruction or docker run flag that defines a command Docker runs periodically inside a container to determine if it is healthy. Containers can be starting, healthy, or unhealthy — used by orchestrators for traffic routing decisions.
Docker Health Check — Is Your Container Actually Working?
What Is a Docker Health Check in Simple Terms?
A container can be running without actually working. PostgreSQL might be up but still initialising. Your API might be running but stuck in a deadlock. A health check is a command Docker runs periodically inside the container — if it succeeds (exit code 0), the container is healthy. If it fails repeatedly, Docker marks it as unhealthy.
Orchestrators use health status for routing — Kubernetes does not send traffic to pods that are not healthy.
+------------------------------------------+| Container starts || Status: starting |+------------------------------------------+ | (after start_period) | | health check runs (every interval) | v+------------------------------------------+| Exits 0 repeatedly -> healthy || Exits non-0 repeatedly -> unhealthy |+------------------------------------------+HEALTHCHECK in Dockerfile
# HTTP endpoint checkHEALTHCHECK --interval=30s --timeout=5s \ --start-period=15s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 # wget (available in Alpine, no curl needed)HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider \ http://localhost:8080/health || exit 1 # Parameters:# --interval How often to run (default: 30s)# --timeout Max time for check to complete (default: 30s)# --start-period Grace period before first check (default: 0s)# --retries Failures before unhealthy (default: 3)Health Check Commands by Service
# PostgreSQLHEALTHCHECK CMD pg_isready -U postgres -d mydb || exit 1 # RedisHEALTHCHECK CMD redis-cli ping | grep -q PONG || exit 1 # MySQLHEALTHCHECK CMD mysqladmin ping -h localhost || exit 1 # RabbitMQHEALTHCHECK --start-period=30s \ CMD rabbitmq-diagnostics ping || exit 1 # Custom application checkHEALTHCHECK CMD node /app/scripts/healthcheck.js || exit 1Monitoring Health Status
# See health status in docker psdocker ps# NAME STATUS# payment-api Up 5m (healthy)# postgres Up 5m (healthy)# redis Up 2m (unhealthy) <- problem! # See health check logs for unhealthy containerdocker inspect redis \ --format '{{json .State.Health}}' | jq# {# "Status": "unhealthy",# "FailingStreak": 3,# "Log": [{# "ExitCode": 1,# "Output": "Could not connect to Redis"# }]# }# The Output field tells you exactly why the check is failing # In Docker Compose — use with depends_onservices: api: depends_on: postgres: condition: service_healthy # waits for healthy statusTipThe
start_periodparameter is critical for services that take time to initialise. Set it to the maximum time your service needs to start — typically 10s for web services and 30s for databases. Withoutstart_period, health check failures during normal startup count toward the failure threshold and can mark healthy services as unhealthy.
Frequently Asked Questions
How does a Docker health check differ from just checking if the container process is still running?
Docker's default notion of 'running' only means the main process hasn't exited — it says nothing about whether the app inside is actually serving traffic correctly. A HEALTHCHECK instruction runs a real command (like `curl -f http://localhost/health`) on an interval and tracks state transitions through starting, healthy, and unhealthy, which is critical for apps that can hang, deadlock, or lose a database connection while the process itself stays alive.
What's a common health check mistake that causes false 'unhealthy' flapping?
Setting the check interval or timeout too aggressively for an app with variable response times under load, so a healthy-but-momentarily-slow container gets marked unhealthy and cycled out of rotation, adding load to remaining containers and making the situation worse. Also common: forgetting `start_period`, which gives slow-starting apps (e.g. those doing cache warmup) a grace period before failed checks count against the retry threshold — without it, a normal cold start can be misread as a crash loop.