
A Docker container can be running while the application inside it is broken. A web server may have stopped responding, a database may still be starting, or an API may be returning errors even though the container process has not exited. Docker health checks help you detect that difference.
This guide explains how Docker health checks work, how to add one to a Dockerfile or Compose file, how to read the result, and what to do when a container becomes unhealthy.
What is a Docker health check?
A health check is a command that Docker runs inside a container at regular intervals. The command must return an exit code:
- 0 means the application is healthy.
- 1 means the application is unhealthy.
- Any other result is treated as an error or unknown result, depending on the check state.
Health checks are different from container status. A container can be running but unhealthy. The container process is still alive, but the health-check command has failed.
Check whether a container is healthy
List containers and include their health status:
docker ps
Look for a status such as Up 2 minutes (healthy) or Up 2 minutes (unhealthy). For a detailed status, inspect the container:
docker inspect --format='{{.State.Health.Status}}' container_name
To view the recent health-check attempts and their output:
docker inspect --format='{{json .State.Health}}' container_name
On systems with jq, the output is easier to read:
docker inspect container_name | jq '.[0].State.Health'
Add a health check to a Dockerfile
You can define a default health check in a Dockerfile with the HEALTHCHECK instruction. This example checks a web application running on port 8080:
FROM nginx:alpine
COPY healthcheck.sh /usr/local/bin/healthcheck.sh
RUN chmod +x /usr/local/bin/healthcheck.sh
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3
CMD ["/usr/local/bin/healthcheck.sh"]
The script could use wget to request a local health endpoint:
#!/bin/sh
wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/health || exit 1
exit 0
Build and run the image:
docker build -t my-web-app .
docker run -d --name my-web-app -p 8080:8080 my-web-app
Use a health endpoint that checks the real application, not just whether a process exists. A useful endpoint might verify that the application can load its configuration and reach required dependencies.
Health-check options explained
The most useful HEALTHCHECK options are:
--interval=30s: wait 30 seconds between checks.--timeout=5s: fail a check that takes longer than five seconds.--start-period=20s: allow the application time to start before failures count.--retries=3: mark the container unhealthy after three consecutive failures.
Choose values based on the application startup time. A database or large web application may need a longer start-period than a small static server.
Add a health check with Docker Compose
Docker Compose lets you define a health check next to the service configuration. This example checks a web service:
services:
web:
image: nginx:alpine
ports:
- "8080:80"
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://127.0.0.1/ || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
Start the service and check its state:
docker compose up -d
docker compose ps
Depending on your Compose version, docker compose ps displays the health state. You can always use docker inspect with the container name when you need the complete check history.
Use health checks for service dependencies
A health check is especially useful when one service depends on another. For example, an application should wait for a database to become healthy before it starts accepting traffic:
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: change-this-password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
app:
image: my-app:latest
depends_on:
db:
condition: service_healthy
This controls startup ordering. It does not guarantee that the database will remain healthy forever or that the application can handle every later outage. The application should still implement connection retries and graceful error handling.
Why is my container unhealthy?
Start by reading the health-check output:
docker inspect --format='{{range .State.Health.Log}}{{.ExitCode}}: {{.Output}}{{"
"}}{{end}}' container_name
Common causes include:
- The health-check URL or port is incorrect.
- The check uses a command that is not installed in the image.
- The application needs more time to start.
- The application is listening on a different interface or port.
- The check requires authentication or environment variables.
- A dependency such as a database or cache is unavailable.
Run the same command manually inside the container to reproduce the problem:
docker exec -it container_name sh
wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/health
Minimal images often do not include curl, wget, or a shell. Either install the tool deliberately, use an available command, or copy a small health-check program into the image.
Does Docker restart an unhealthy container automatically?
No. The standard Docker restart policies respond when the container process exits. They do not restart a container merely because its health status changes to unhealthy.
You can still configure a restart policy for process crashes:
docker run -d --restart unless-stopped --name my-web-app my-web-app
For Compose:
services:
web:
image: my-web-app:latest
restart: unless-stopped
If you need an unhealthy service to be replaced or restarted, use an orchestrator such as Kubernetes, a service manager, or a carefully designed monitoring script. Do not add an automatic restart loop before understanding why the health check is failing.
Disable or override a health check
To disable a health check inherited from an image in Docker Compose:
services:
web:
image: example/web
healthcheck:
disable: true
Use this only when you have a better external check or the image’s default check is incompatible with your environment. Removing the check can hide real application failures.
Best practices for reliable Docker health checks
- Check the application, not only the process.
- Keep the command fast and lightweight.
- Use a dedicated endpoint such as
/healthor/ready. - Allow enough startup time with
start-period. - Do not make a deep dependency check so strict that a brief database delay marks every service unhealthy.
- Review health-check output before increasing retries or adding automatic restarts.
- Keep secrets out of health-check command lines where possible.
Quick reference
| Task | Command or setting |
|---|---|
| Show container health | docker inspect --format='{{.State.Health.Status}}' NAME |
| Show health-check history | docker inspect NAME |
| Run a check manually | docker exec -it NAME sh |
| Define an image check | HEALTHCHECK |
| Define a Compose check | healthcheck: |
| Wait for a healthy dependency | condition: service_healthy |
| Restart after process exit | restart: unless-stopped |
Conclusion
Docker health checks provide a reliable signal about whether the application inside a container is ready and responding. Add a small, meaningful check, inspect failures with docker inspect, and use Compose dependency conditions when startup order matters. Remember that an unhealthy status is a diagnosis signal—not an automatic restart command.