Skip to content

Dockerfile — HEALTHCHECK


Your container can be running and still be completely broken.

That’s the problem HEALTHCHECK solves.

Docker already knows whether a container’s main process is still running. But “the process is running” and “the application is actually working” aren’t the same thing.

A web server might be stuck. A database connection might be broken. An application might have started but still not be ready to serve requests.

HEALTHCHECK gives Docker a way to ask:

“Is this container actually healthy?”

What HEALTHCHECK Does

HEALTHCHECK tells Docker how to test the running container.

For example, if your application exposes a health endpoint:

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD curl --fail http://localhost:8080/health || exit 1

Docker periodically runs that command inside the container.

  • If the command succeeds, the container is considered healthy.
  • If it repeatedly fails, Docker marks the container as unhealthy.

Checking Health Status: docker container ls

You can see the result with:

docker container ls

For a container with a health check, docker container ls will show the health status in the STATUS column:

CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS                      PORTS                    NAMES
a1b2c3d4e5f6   myapp     "node server.js"        2 minutes ago   Up 2 minutes (healthy)      0.0.0.0:8080->8080/tcp   myapp

If the health check is failing, you’ll see:

CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS                        PORTS                    NAMES
a1b2c3d4e5f6   myapp     "node server.js"        2 minutes ago   Up 2 minutes (unhealthy)    0.0.0.0:8080->8080/tcp   myapp

And while Docker hasn’t completed enough checks yet, you may see:

STATUS
Up 5 seconds (health: starting)

So the useful part to look for is:

(healthy)
(unhealthy)
(health: starting)

Checking Health Detailed Report: docker inspect

docker image inspect shows the healthcheck configuration, not the current health report of a running container — docker container inspect shows it.

Image inspect

If your Dockerfile contains:

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD curl --fail http://localhost:8080/health || exit 1

then:

docker image inspect myapp

will show something like:

"Config": {
    ...
    "Healthcheck": {
        "Test": [
            "CMD-SHELL",
            "curl --fail http://localhost:8080/health || exit 1"
        ],
        "Interval": 30000000000,
        "Timeout": 5000000000,
        "Retries": 3
    }
}

That’s basically Docker saying:

“This image has this healthcheck configured.”

Container inspect

The actual health report belongs to the running container:

docker container inspect myapp-container

You’ll find something like:

"State": {
    "Status": "running",
    "Running": true,
    "Health": {
        "Status": "healthy",
        "FailingStreak": 0,
        "Log": [
            {
                "Start": "2026-08-10T10:00:00.000000000Z",
                "End": "2026-08-10T10:00:00.050000000Z",
                "ExitCode": 0,
                "Output": "..."
            }
        ]
    }
}

So the distinction is:

docker image inspect <image>
        ↓
"What HEALTHCHECK does this <image> have configured?"

docker container inspect <container>
        ↓
"What's the current HEALTH of this <container>?"

And if you only want the health status:

docker container inspect --format='{{.State.Health.Status}}' myapp-container

Output:

healthy

Important

docker container ls shows the health status, but docker container inspect gives you the detailed health-check output, including recent check results and error messages.

Running Is Not the Same as Healthy

Consider this container:

CMD ["node", "server.js"]

If server.js is still running, Docker considers the container running.

But imagine the application has stopped responding to requests while the Node process itself is still alive.

From Docker’s perspective:

Process running  →  Container running

But what you actually care about is:

Process running
       +
Application responding
       ↓
Container healthy

That’s why HEALTHCHECK exists.

It lets you test something closer to what users or other services actually depend on.

What a Health Check Should Test

A good health check should test the thing that proves the application is usable.

For a web application, that might be:

HEALTHCHECK CMD curl --fail http://localhost:8080/health || exit 1

For another service, it could be a command that verifies the service can actually answer a request or perform a lightweight operation.

The important part is that the command should:

  • Exit with 0 when the application is healthy.
  • Exit with a non-zero status when it isn’t.

Docker uses that exit status to determine the health state.

The result is essentially:

0       → healthy
non-zero → unhealthy

The Timing Options

You can control how Docker performs the check.

For example:

HEALTHCHECK \
  --interval=30s \
  --timeout=5s \
  --start-period=10s \
  --retries=3 \
  CMD curl --fail http://localhost:8080/health || exit 1

These options answer different questions:

  • --interval — How often should Docker run the check?
  • --timeout — How long can one check take?
  • --start-period — How much time should the application get to start before failures count?
  • --retries — How many consecutive failures are needed before Docker marks it unhealthy?

That --start-period is particularly useful for applications that need some time to initialize.

You don’t want a perfectly normal startup sequence to immediately look like a failure.

HEALTHCHECK Doesn’t Restart the Container

This is an important distinction.

If a container becomes unhealthy, Docker marks it:

unhealthy

But HEALTHCHECK itself does not mean:

“Restart this container when the check fails.”

It’s a monitoring signal, not a restart policy.

What happens next depends on the system running your container.

For example, an orchestrator can use health information to decide whether a container should receive traffic or be replaced.

So think of HEALTHCHECK as answering:

“What’s the current health of this container?”

Not:

“What should Docker do when it becomes unhealthy?”

When to Reach for HEALTHCHECK

Reach for HEALTHCHECK when being alive isn’t enough to prove that your application is working.

It’s particularly useful for:

  • Web applications with a health endpoint.
  • APIs that need to confirm they’re responding.
  • Services that take time to initialize.
  • Containers where another system needs a health signal.

You might skip it when the container is extremely simple and the process itself is a sufficient indication of health.

For example, if the container runs a short-lived command whose success or failure is already represented by its exit code, a separate health check may not add much value.

A Common Mistake

Don’t make the health check more complicated than the application needs.

For example, you don’t necessarily need to test every dependency:

Application
   ↓
Database
   ↓
Redis
   ↓
External API
   ↓
Third-party service

If your health check requires everything to be perfect, a temporary problem in one dependency could make the entire container appear unhealthy.

Instead, decide what healthy means for this service.

Sometimes that means:

“The process is running and the application can accept requests.”

Sometimes it means:

“The application can also reach a critical dependency.”

The right check depends on what the container is responsible for.

Note

A good HEALTHCHECK is small, fast, and meaningful. Test the minimum thing that proves the service is usable. Don’t turn the health check into a second application.

Wrapping Up

HEALTHCHECK adds something Docker can’t get from the process status alone: application-level health.

A container can be:

running
healthy

or:

running
unhealthy

And that’s the key idea.

Docker already knows whether the container’s process is alive. HEALTHCHECK lets you tell Docker whether the service itself is actually working.

So when you reach for it, ask:

“Could this container be running while the application is broken?”

If the answer is yes, HEALTHCHECK is probably worth adding.

Last updated on