Docker Compose Healthcheck: How to Check Container Health

Server racks in a data center used as a hero image for a WordPress on ARM VPS with Docker guide

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 /health or /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

TaskCommand or setting
Show container healthdocker inspect --format='{{.State.Health.Status}}' NAME
Show health-check historydocker inspect NAME
Run a check manuallydocker exec -it NAME sh
Define an image checkHEALTHCHECK
Define a Compose checkhealthcheck:
Wait for a healthy dependencycondition: service_healthy
Restart after process exitrestart: 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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top