How To Successfully Implement A Healthcheck In Docker Compose
Tags: docker, devops, programming, webdev
Published: 2022-11-18 00:07:02

**Knowing if your Docker service is running is important. A broken service can lead to a big incident! Use Docker health checks to avoid this!**

[Paul Knulst](https://www.paulsblog.dev/) in [Docker](https://www.paulsblog.dev/tag/docker/) • Dec 20, 2021 • 5 min read

* * *

1\. Introduction
----------------

A health check is exactly what they sound like - a way of checking the health of a resource. In the case of Docker, a health check is used to determine the health of a running container.

When a health check command is created, it defines how a container can be tested to see if it is working correctly. With no health check defined, Docker cannot know whether or not the services running within your container are actually started or not.

While working with Docker, two ways of defining a health check exist:

*   Dockerfile
*   Docker-Compose file.

I will cover **only Docker-Compose Healthchecks** within this article.

2\. An example using Nginx
--------------------------

Let’s see how health checks work by using a simple Nginx web service. To create a very simple website we need three files:

A docker-compose.very-simple-web.yml, a Dockerfile, and an index.html:
```yaml
version: '3.4'
services:
  web:
    image: very-simple-web
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "80:80"
    restart: unless-stopped
```

```docker
FROM nginx
COPY html /usr/share/nginx/html
```   

```html
<html>
<head>
    <title>Hello World FTP</title>
</head>
<body>
  Hello World
</body>
</html>

```
The three files must be saved in this structure

| ![Sample directory structure of website to build with docker-compose](https://www.paulsblog.dev/content/images/2022/09/image--17-.webp)|
|:--:|
| <center> *Sample directory structure of website to build with docker-compose* </center> |

Now let’s create the service:
```bash
docker-compose up -d --build
```

If you open a web browser to localhost you should see “Hello World”

3\. Why do we need a health check?
----------------------------------

Well, in this special case normally we don't need any health check because we are just serving a simple website with Nginx. But in a production environment, it is possible that we run multiple processes that could crash.

In this case, we could use our website, but the Docker service will still be running even though we cannot access the website.

If this service is running within a Docker Swarm, the Swarm will still think everything is working correctly because the container is in a running state. This leads to a problem that the Swarm thinks everything is fine and will not restart the container so that it will be working again.

4\. Configure a health check within the compose file
----------------------------------------------------

As an example, the docker-compose.very-simple-web.yml will be extended by a simple `curl` based health check (will be explained later):
```yaml
version: '3.4'
services:
  web:
    image: very-simple-web
    build:
      context: ./
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "80:80"
    healthcheck:
      test: curl --fail http://localhost || exit 1
      interval: 60s
      retries: 5
      start_period: 20s
      timeout: 10s
```

The Docker Compose Healtcheck contains five properties:

*   **test:** This property specifies the command that will be executed and is the **__health check__** of the container. This command HAS TO be available and working if everything is set up correctly.
*   **interval:** This property specifies the number of seconds to initially wait before executing the health check and then the frequency at which subsequent health checks will be performed.
*   **timeout:** This property specifies the number of seconds Docker awaits for your health check command to return an exit code before declaring it as failed.
*   **retries:** This property specifies the number of consecutive health check failures required to declare the container as ****unhealthy****.
*   **start\_period:** This property specifies the number of seconds your container needs to bootstrap. During this period, health checks with an exit code greater than zero won’t mark the container as **unhealthy**; however, a status code of 0 will mark the container as **healthy**.

Note that this health check is based on `curl`. To use this health check you have to guarantee that `curl` is installed within the image used for running the service.

If `curl` is not available another often-used health check is based on `wget` and can look like this:
```bash
wget --no-verbose --tries=1 --spider http://localhost || exit 1
```

Just replace the `curl` command in the prior defined compose file.

5\. Often used Docker health checks
-----------------------------------

### 5.1 WGET
```yaml
healthcheck:
  test: wget --no-verbose --tries=1 --spider http://localhost || exit 1
  interval: 60s
  retries: 5
  start_period: 20s
  timeout: 10s
```

### 5.2 CURL
```yaml
healthcheck:
  test: curl --fail http://localhost || exit 1
  interval: 60s
  retries: 5
  start_period: 20s
  timeout: 10s
```

### 5.3 What to do if CURL/WGET is not available?

If you use an image that doesn’t have `curl` or `wget` installed you need to install it. This can be achieved by adding an install command to your Dockerfile.

**curl**
```bash
RUN apk --update --no-cache add curl
```

**wget**
```bash
RUN apt-get update && apt-get install -y wget
```

But keep in mind that if you add `curl` or `wget` you could also **add all the attack surfaces of those two tools**.

A good workaround will be creating your own program which will then be included in the docker health check test command.

6\. A custom health check
-------------------------

In comparison to `curl` or `wget` the custom health check gets over all the issues of using an external tool:

1.  You’re using the same runtime as your actual app, so there are no additional prerequisites for your health check that has to be installed
2.  You can decide which logic you want in your health check and it can stay private, so only the Docker platform can execute that code.

The downside is that you have to create and maintain a separate piece of code. But as this will be written in the same language as your app it should be much easier to achieve than developing a complex `curl` or `wget` command (If it is complex).

7\. Closing Notes
-----------------

I hope you learned that having your Docker container started does not necessarily mean that your application is running and works how it was designed. Docker health checks can be implemented quickly to identify possible bugs in your software before they become a real problem.

**Next time you create a Docker-Compose file, consider adding a health check.**

This article was originally published on my blog at [https://www.paulsblog.dev/how-to-successfully-implement-a-healthcheck-in-docker-compose/](https://www.paulsblog.dev/how-to-successfully-implement-a-healthcheck-in-docker-compose/)

Feel free to connect with me on [my personal blog](https://www.paulsblog.dev), [Medium](https://medium.knulst.de), [LinkedIn](https://www.linkedin.com/in/paulknulst/), [Twitter](https://twitter.com/paulknulst), and [GitHub](https://github.com/paulknulst).


* * *

Did you find this article valuable? Want to support the author? (... and support development of current and future tutorials!). You can sponsor me on [Buy Me a Coffee](https://buymeacoffee.com/paulknulst) or [Ko-Fi](https://ko-fi.com/paulknulst). Furthermore, you can become a free or paid member by [signing up to my website](https://www.paulsblog.dev/#/portal). See the [contribute page](https://www.paulsblog.dev/contribute/) for all (free or paid) ways to say thank you!

* * *

Photo by [Online Marketing](https://unsplash.com/es/@impulsq?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)