2026年11月14日

2026年11月14日

Docker Health Checks: Set Up HEALTHCHECK, Compose and docker run

This article is a localized edition of the Japanese original. Read the Japanese original

A Docker health check runs a given command inside the container at regular intervals and uses the result to decide whether the container is "responding correctly". Even when the process is running, it can tell you that the web server is returning errors or that the database is not yet accepting connections.

This article explains three ways to configure a health check (Dockerfile, Docker Compose and docker run) and how to check the results. Behavior was confirmed as of October 2026 with Docker Engine 29, Docker Compose v5 and the nginx:1.28 and mariadb:11 images.

How health checks work

A container with a health check is in one of these three states.

State Meaning
starting Just started. No verdict yet
healthy The check command succeeded (exit code 0)
unhealthy The check command failed a set number of times in a row

The check command signals success with exit code 0 and failure with 1 (2 is reserved; do not use it).

Note: Docker Engine on its own does not restart a container when it becomes unhealthy. The --restart policy applies only when the container's process exits. How to deal with this is explained later in "Unhealthy containers are not restarted".

Configure it in a Dockerfile

Writing a HEALTHCHECK instruction in a Dockerfile adds the health check to every container created from that image.

FROM nginx:1.28

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --start-interval=2s --retries=3 \
  CMD curl -fsS -o /dev/null http://localhost/ || exit 1

The -f option of curl makes it exit with code 22 when the HTTP status is 400 or higher. || exit 1 turns every failure into exit code 1.

Option Default Meaning
--interval 30s Time between checks
--timeout 30s Time limit for one check. Exceeding it counts as a failure
--start-period 0s Grace period after startup. Failures during it are not counted (a success makes it healthy immediately)
--start-interval 5s Time between checks during the grace period (Docker Engine 25.0 and later)
--retries 3 Consecutive failures needed to become unhealthy

Without --start-interval, the first check runs only after --interval has passed since startup, so the container stays starting for about 30 seconds before it becomes healthy.

If the base image defines a health check and you want to disable it, write HEALTHCHECK NONE.

Make sure the check command exists in the image

The check command runs inside the container, so it must exist in the image. The official nginx image includes curl, but many images include neither curl nor wget. Check beforehand.

docker run --rm nginx:1.28 sh -c 'command -v curl wget'

If nothing is printed, neither is installed. In that case, install one in the Dockerfile or use a check command the application itself provides (for example healthcheck.sh in the official mariadb image).

Configure it in Docker Compose

In a Compose file (compose.yaml), write healthcheck for each service.

services:
  db:
    image: mariadb:11
    environment:
      MARIADB_ROOT_PASSWORD: example
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 30s
      start_interval: 2s

  web:
    image: nginx:1.28
    ports:
      - "8080:80"
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS -o /dev/null http://localhost/ || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
    depends_on:
      db:
        condition: service_healthy
  • If test starts with CMD, the remaining elements are run as the command as-is. With CMD-SHELL, the following string is run by the shell (/bin/sh -c), so you can use || and the like
  • condition: service_healthy under depends_on starts web only after db is healthy. This prevents the app from starting before the database is ready and failing to connect
  • To disable a health check defined in the image, write disable: true under healthcheck:

When you bring it up, you can see web start only after db becomes Healthy.

docker compose up -d
 Container hc-db-1 Started
 Container hc-db-1 Waiting
 Container hc-db-1 Healthy
 Container hc-web-1 Starting
 Container hc-web-1 Started

You do not need a line such as version: '3.8' at the top of the Compose file. Current Docker Compose ignores it and warns that it is "obsolete".

Configure it with docker run

You can also add a health check when creating a container, without rebuilding the image.

docker run -d --name my-nginx \
  --health-cmd='curl -fsS -o /dev/null http://localhost/ || exit 1' \
  --health-interval=30s \
  --health-timeout=5s \
  --health-retries=3 \
  --health-start-period=10s \
  nginx:1.28

--health-cmd is run by the shell. To disable a health check defined in the image, add --no-healthcheck.

Health check settings are fixed when the container is created. You cannot add one to a running container later, so recreate the container to change them.

Check the status

The STATUS column of docker ps shows the health check state.

docker ps --format '{{.Names}}  {{.Status}}'
my-nginx  Up 5 seconds (healthy)

docker inspect shows the detailed results.

# State only
docker inspect --format '{{.State.Health.Status}}' my-nginx

# Exit codes and output of recent checks
docker inspect --format '{{json .State.Health}}' my-nginx
{"Status":"healthy","FailingStreak":0,"Log":[{"Start":"2026-10-29T17:39:31.069523955Z","End":"2026-10-29T17:39:31.146682497Z","ExitCode":0,"Output":""}]}

FailingStreak is the number of consecutive failures, and ExitCode and Output in Log are the exit code and output of the check command. When the container becomes unhealthy, Output holds the reason for the failure. To show only the exit codes and output side by side:

docker inspect --format '{{range .State.Health.Log}}{{.ExitCode}} {{.Output}}{{end}}' my-nginx
1 curl: (22) The requested URL returned error: 403

You can also list only unhealthy containers, or watch events when the state changes.

# Show only unhealthy containers
docker ps --filter health=unhealthy

# Keep showing health state changes (Ctrl+C to stop)
docker events --filter event=health_status

Unhealthy containers are not restarted

In testing, a container started with --restart unless-stopped became unhealthy, but its restart count (RestartCount) stayed at 0. Docker Engine on its own does not restart containers based on health check results.

If you want containers restarted when they are unhealthy, these are your options.

  • Run it as a Docker Swarm service: in Swarm mode, an unhealthy task (container) is stopped and replaced with a new one
  • Use Kubernetes: a container whose liveness probe fails is restarted
  • Restart it yourself: run a command that finds and restarts unhealthy containers periodically, for example from cron
# Restart all unhealthy containers
docker ps -q --filter health=unhealthy | xargs -r docker restart

A restart fixes only transient problems. If a container keeps becoming unhealthy, investigate with the Output from docker inspect and the container logs (docker logs).

Things to keep in mind

  • Keep checks light: the check runs inside the container at every interval. Avoid heavy work (such as large database queries) and use a page or command that just returns a response
  • Do not depend on external services: if the check includes an external API or similar, an outage of that service makes your own container unhealthy too
  • Match --timeout and --start-period to real behavior: for apps that start slowly, a --start-period that is too short counts failures during startup and the container becomes unhealthy
  • Check via localhost: the check runs inside the container, so connect to the port the container listens on, not the host port (for example 8080)

Summary

  • A health check runs a command inside the container periodically: exit code 0 means healthy, and repeated failures mean unhealthy
  • You can configure it with HEALTHCHECK in a Dockerfile, healthcheck in Compose, or --health-* with docker run
  • --start-period and --start-interval tune the verdict right after startup
  • depends_on with condition: service_healthy in Compose starts a service only after its dependency is ready
  • Docker Engine alone does not restart unhealthy containers. Handle this with Swarm, Kubernetes or your own mechanism

Related articles:

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲