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 1Docker 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 lsFor 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 myappIf 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 myappAnd 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 1then:
docker image inspect myappwill 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-containerYou’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-containerOutput:
healthyImportant
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 runningBut what you actually care about is:
Process running
+
Application responding
↓
Container healthyThat’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 1For 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
0when 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 → unhealthyThe 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 1These 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:
unhealthyBut 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 serviceIf 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
healthyor:
running
unhealthyAnd 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.