Understand healthcheck in Docker Compose

Gabriel Manricks
Gabriel ManricksChief Architect, ClearX
Published: February 5, 2024

In Docker Compose, a health check is a configuration option that allows you to define a command to periodically check the health of a container. This feature helps ensure that the container is not only running but also in a "healthy" state, meaning it's ready to handle requests or perform its intended functions.

The short answer

To define a health check for a specific service, you can specify the healthcheck  property within the service definition in the compose.yaml  file as follows:

Bash
services:
  <service_name>:
    image: <image_name>
    healthcheck:
      test: <command>

Where command  is the command that will be periodically executed by Compose to check whether the service is healthy or unhealthy. The container is considered healthy if the exit status of this command is 0 , and unhealthy otherwise.

For example, the following configuration checks whether the webserver  service is healthy by sending an HTTP request to the nginx  server located at the localhost address:

Bash
services:
  webserver:
    image: nginx
    healthcheck:
      test: "curl -f http://localhost"

Note that the -f  flag will cause the command to fail by returning a non-zero exit code if the HTTP request' status code represents an error (e.g. 500 ).

Easily retrieve this syntax using the Warp AI feature

If you’re using Warp as your terminal, you can easily retrieve this syntax using the Warp AI feature:

Entering docker compose health checks  in the AI question input will prompt a human-readable step-by-step guide, including code snippets.

Customizing the health check settings

By default, Docker will run the test command every 30 seconds, and it will give the command 30 seconds to complete before considering the health check failed. If the health check command fails 3 times in a row, the container is then considered "unhealthy".

To change these settings, you can use the following properties:

  • interval : the period between health checks (default 30s ).
  • timeout : the amount of time given to the command to complete (default 30s ).
  • retries : the number of consecutive fails before considered unhealthy (default 3 ).

For example:

Bash
healthcheck: 
  test: "curl -f http://localhost"
  interval: 15s
  timeout: 1m
  retries: 2

In some cases, you may have a container that takes a long time to initially start, which would cause the service to be considered unhealthy using the regular interval settings.

To change these settings, you can use the following properties:

  • start\_period : the grace period during which Compose will not consider the health check failures (default 0s ). However, if the health check succeeds, then the container is marked healthy, and the grace period ends early.
  • start\_interval : the period between health checks during the grace period (default 5s ).

For example:

Bash
healthcheck: 
    test: "curl -f http://localhost"
    start_period: 1m
    start_interval: 10s

Customizing the test command format

In Docker Compose, health check test commands can be executed using two different keywords: CMD  and CMD-SHELL .

Executing test commands using CMD

When using the CMD  form, the test command is split into multiple elements, where the first element is the command itself, and the subsequent elements are its options and arguments. In this form, the test command is executed directly without involving a shell.

For example:

Bash
healthcheck: 
  test: ["CMD", "curl", "-f", "http://localhost"]

Executing test commands using CMD-SHELL

When using the CMD-SHELL  form, the test command is specified as a single string. In this form, the test command is executed in a subshell, which allows you to leverage the shell's environment and features like variable substitution, pipes, or redirects.

For example:

Bash
healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost"]

Note that, in this form, the above command is equivalent to the following one:

Bash
healthcheck:
  test: "curl -f http://localhost"

Managing dependencies with health checks

One of the main uses of health checks is to manage the dependencies between containers. For example, if you have a web server that depends on a MySQL server, you might need to make sure that the MySQL server is up and running before starting the web server.

This can be done using the depends\_on  property, waiting for the condition service\_healthy  as follows:

Bash
services:
  mysql:
    image: mysql:8.2
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: test
      MYSQL_PASSWORD: test
    healthcheck:
      test: "mysql --user=$$MYSQL_USER --password=$$MYSQL_PASSWORD --execute \"SHOW DATABASES;\" || exit 1"
      interval: 10s
      timeout: 10s
      retries: 5
      start_period: 20s
 test_site:
    image: nginx
    depends_on:
      mysql:
        condition: service_healthy

In this example, there is a health check set on the MySQL service, which simply tries to execute a simple query to see if the DB is responding.

Under the test\_site  service, there is the depends\_on  field with a single dependency on the service named mysql  and a condition of service\_healthy , which means when the service is marked healthy. Running this, you will see that at first, only the MySQL service will be started, and the test\_site container will wait until MySQL is marked healthy to begin running.

To learn more about the dependencies between services in Docker Compose, you can read our in-depth article on how to use the depends\_on field in Compose.

Viewing the status and logs of a health check

To view the status of the health checks along with the logs from each test, you can use the following docker inspect  command:

Bash
$ docker inspect --format='{{json .State.Health}}' <container_name_or_id>

Where:

  • The docker inspect  command returns a JSON output with all the information about a specific container.
  • The --format  option extracts just the subfield in the JSON object with the health check info, which is located under the Health  key that is inside an object called State .

This output can be piped to a JSON parsing CLI like jq  or fx  to pretty print the JSON like in the following example:

Bash
$ docker inspect --format "{{json .State.Health }}" test-docker-mysql-1 | jq

Checking for a specific HTTP status code

To create a health check that checks for a specific HTTP status code, you can combine the curl  command to make a request and the grep  command to filter the result as follows:

Bash
services:
  <service_name>:
    image: <image_name>
    healthcheck:
      test: "curl -s -I http://localhost | head -n 1 | grep 200"

Where:

  • The curl -s -l  command is used to only print the response’s headers while avoiding printing the request’s progress or errors.
  • The head -n 1  command is used to only print the first line of the output generated by the curl  command.
  • The grep  command is used to verify that the filtered output includes the status code 200. If the line doesn’t include the specified code, it will return a non-zero exit code, triggering a failed health check.

Checking for an open TCP port

A basic health check that can be done on a variety of containers is to check if a TCP port is open and accepting connections.

Attempting a TCP connection with Netcat

If the container you want to perform a health check on has access to the nc  utility (short for Netcat), you can use the -z  flag to attempt a connection on a specified port without actually sending any data to it:

Bash
healthcheck:
  test: ["CMD", "nc", "-z", "localhost", "5672"]

Attempting a TCP connection with Bash

If the container you want to perform a health check on has access to the bash  utility, you can use the following command to write an empty string to a pseudo file in the /dev/tcp  directory, which will attempt a connection on the TCP socket identified by the specified ip\_address  and port :

Bash
healthcheck:
  test: ["CMD", "bash", "-c", "echo -n '' > /dev/tcp/<ip_address>/<port>"]

Where:

  • The -c  option is used to tell bash  to execute a command.
  • The -n  option is used to tell echo  to write without appending a newline character.

For example, this command will attempt a TCP connection to the localhost identified by the IP address 127.0.0.1  and the port 5672 :

Bash
healthcheck:
  test: ["CMD", "bash", "-c", "echo -n '' > /dev/tcp/127.0.0.1/5672"]

It is worth noting that even though this command uses a shell to run a command, you can’t use CMD-SHELL  as this feature doesn’t work in all shells (e.g. doesn’t work with sh) so you need to manually specify the use of bash .

Written by
Gabriel Manricks
Gabriel ManricksChief Architect, ClearX
Filed under

Related articles


Learning Docker (The Easy Way) Using LazyDocker & Warp

A concise guide to learning Docker using Lazydocker. Highlights Docker’s benefits and takes advantage of Warp's AI features for a quick setup.

Run SSH In Docker

Learn how to launch and connect to a containerized SSH server in Docker using password-based authentication and SSH keys.

Remove a Docker Image

Learn how to remove a Docker image locally, on a Docker registry, and on Artifactory.

Override the Container Entrypoint With docker run

Learn how to override and customize the entrypoint of a Docker container using the docker run command.

The Dockerfile ARG Instruction

Learn how to define and set build-time variables for Docker images using the ARG instruction and the --build-arg flag.

Start a Docker Container

Learn how to start a new Docker container from an image in both the foreground and the background using the docker-run command.

Stop All Docker Containers

How to gracefully shutdown running containers and forcefully kill unresponsive containers with signals in Docker using the docker-stop and docker-kill commands.

Set Docker Container Hostname

Learn how to set, change and match a docker container hostname.

How To Use An .env File In Docker Compose

Learn how define and pass environment variables to Docker containers using an .env file in Docker Compose.

Use An .env File In Docker

Learn how to write and use .env files in Docker to populate the environment of containers on startup.

Restart Docker Containers

Learn how to restart Docker containers automatically with restart policies and manually using the docker restart, docker start, docker stop and docker kill commands.

Run Bash Shell In Docker

Start an interactive shell in Docker container