Understand healthcheck in Docker Compose
Gabriel Manricks
Chief Architect, ClearX
Published: 2/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:
services:
<service_name>:
image: <image_name>
healthcheck:
test: <command>
Run in Warp
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:
services:
webserver:
image: nginx
healthcheck:
test: "curl -f http://localhost"
Run in Warp
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:
healthcheck:
test: "curl -f http://localhost"
interval: 15s
timeout: 1m
retries: 2
Run in Warp
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:
healthcheck:
test: "curl -f http://localhost"
start_period: 1m
start_interval: 10s
Run in Warp
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:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
Run in Warp
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:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost"]
Run in Warp
Note that, in this form, the above command is equivalent to the following one:
healthcheck:
test: "curl -f http://localhost"
Run in Warp
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:
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
Run in Warp
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:
$ docker inspect --format='{{json .State.Health}}' <container_name_or_id>
Run in Warp
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:
$ docker inspect --format "{{json .State.Health }}" test-docker-mysql-1 | jq
Run in Warp
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:
services:
<service_name>:
image: <image_name>
healthcheck:
test: "curl -s -I http://localhost | head -n 1 | grep 200"
Run in Warp
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:
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "5672"]
Run in Warp
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 :
healthcheck:
test: ["CMD", "bash", "-c", "echo -n '' > /dev/tcp/<ip_address>/<port>"]
Run in Warp
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 :
healthcheck:
test: ["CMD", "bash", "-c", "echo -n '' > /dev/tcp/127.0.0.1/5672"]
Run in Warp
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
Chief Architect, ClearX
Filed Under
Related Articles
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.
Use An .env File In Docker
Learn how to write and use .env files in Docker to populate the environment of containers on startup.
Run SSH In Docker
Learn how to launch and connect to a containerized SSH server in Docker using password-based authentication and SSH keys.
Launch MySQL Using Docker Compose
Learn how to launch a MySQL container in Docker Compose.
Execute in a Docker Container
Learn how to execute one or multiple commands in a Docker container using the docker exec command.
Expose Docker Container Ports
Learn how to publish and expose Docker container ports using the docker run command and Dockerfiles.
Restart Containers In Docker Compose
Learn how to restart and rebuild one or more containers in Docker Compose.
Output Logs in Docker Compose
Learn how to output, monitor, customize and filter the logs of the containers related to one or more services in Docker Compose
Rename A Docker Image
Learn how to rename Docker images locally and remotely using the docker tag command.