In this article
Your self-hosted dashboard reports a container as healthy. Is that enough to trust the service through its usual hostname or reverse proxy? Not necessarily.
Docker’s health status reports the result of a configured probe. It does not independently certify a database, public hostname, TLS configuration, proxy route, or every application feature. The useful question is not simply, “Is the container healthy?” but, “What path did its healthcheck test?”
Evidence scope: This is a documentation-based explanation using Docker documentation retrieved September 14, 2026. No hands-on availability, timing, false-positive, or recovery tests were performed.
How Docker determines health
A Dockerfile can define a HEALTHCHECK. Docker runs its configured command inside the container and derives health from the command’s exit status:
0: success1: unhealthy2: reserved; do not use
A container with a healthcheck receives a health status separate from its normal container status. It begins as starting, becomes healthy after a successful check, and becomes unhealthy after the configured number of consecutive failures (Dockerfile reference: HEALTHCHECK).
For example:
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost/ || exit 1
For this probe, Docker runs curl and evaluates its exit status. If the command exceeds timeout, Docker treats that attempt as failed. The container becomes unhealthy only after the configured number of consecutive failures (Dockerfile reference: HEALTHCHECK).
Health is therefore a periodic, filtered signal rather than a continuous observation. A healthy status means that the configured probe succeeded and Docker has not since recorded enough consecutive failures to cross the retry threshold.
Docker labels exit code 0 as “healthy and ready for use,” but it assigns that label solely from the configured command’s result. The assurance is only as broad as the command itself.
Compose uses the same mechanism
Compose exposes health configuration at the service level:
services:
app:
image: your-application-image
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
Compose follows Dockerfile healthcheck behavior and defaults while allowing the service definition to override values supplied by the image. It can also disable an image-provided check with NONE or disable: true (Compose file reference: healthcheck).
A Compose test may be a string or a list. List forms begin with CMD, CMD-SHELL, or NONE; a string is equivalent to CMD-SHELL. In every form, the meaning of success comes from what the selected command tests.
What a localhost probe misses
If the example probe passes, it establishes that the command ran inside the container, requested http://localhost/, completed within the timeout, and returned success. That can reveal more than process status alone: Docker notes that a healthcheck may detect a web server that remains running but is stuck and unable to handle connections (Dockerfile reference: HEALTHCHECK).
However, an in-container request to localhost does not inherently traverse:
- public DNS or another device’s network path;
- the host’s published port;
- firewall or forwarding rules;
- reverse-proxy routing;
- TLS termination or certificate handling;
- authentication or database-backed operations; or
- unrelated application features.
That boundary follows from Docker’s documented in-container execution model and localhost example. It does not mean healthchecks can never cover those components. A custom probe could request an external hostname or exercise selected dependencies, but its assurance would still extend only to the routes and operations it performs.
A dashboard can consequently remain locally healthy while its usual hostname fails because a reverse proxy points to the wrong port. The checks follow different paths and answer different questions.
Running, healthy, ready, and reachable
These four ideas are related but not interchangeable:
- Running: The container’s main process has not exited.
- Healthy: The configured probe has passed without enough later failures to change the status.
- Ready: The application can perform the operations its intended consumers require.
- Reachable: A client can access it through the intended network route.
Each check establishes only what it exercises:
| Check layer | What it establishes | Typical blind spot |
|---|---|---|
| Internal healthcheck | A selected command or local endpoint succeeds inside the container | Client routing |
| Dependency healthcheck | The selected dependency probe succeeds | Operations omitted by that probe |
| External check | The tested client route works from the checker’s location | Other features or locations |
“External” need not mean public internet monitoring. For a LAN-only service, it can mean testing from another device through the hostname, reverse proxy, TLS endpoint, and network path that household clients actually use.
Healthchecks and Compose startup
Compose creates services in dependency order, but ordinary startup ordering waits only for a dependency container to run—not for its application to become ready (Compose startup order).
Long-form depends_on can instead gate startup on a dependency healthcheck:
services:
app:
image: your-application-image
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 10s
retries: 5
start_period: 30s
With condition: service_healthy, Compose waits for the database’s configured healthcheck to pass before creating the dependent service (Compose startup order). This improves startup coordination, but it neither broadens the database probe nor verifies every query the application may issue.
Timing and recovery limits
Docker supports interval, timeout, retries, start_period, and start_interval. Failures during start_period do not count toward the retry limit. If a check succeeds during that period, Docker considers the container started and counts subsequent consecutive failures normally. The start-interval option requires Docker Engine 25.0 or later (Dockerfile reference: HEALTHCHECK).
Intervals and retries prevent every brief failure from changing status immediately, but they also delay detection. A service can fail after a successful probe and remain marked healthy until later checks accumulate enough failures.
Health status is also separate from recovery. Docker documents standalone restart policies in terms of containers exiting or stopping, or Docker restarting (Docker restart policies). Do not assume that an unhealthy status alone restarts a standalone container; treat it as a signal unless another configured mechanism acts on it. Swarm service restart behavior is outside this article’s scope.
Decide what evidence you need
Before trusting a healthy status, identify:
- the exact command or endpoint tested;
- what produces a successful exit;
- where the probe runs;
- which dependencies and operations it exercises;
- which client-routing layers it bypasses;
- how intervals and retries delay status changes;
- what, if anything, responds to
unhealthy; and - whether the route clients use is tested separately.
Use internal healthchecks for container-local signaling and Compose startup coordination. When the goal is evidence that users can reach the service, add a check that follows their actual route.
No comments yet