When a minimal image has no curl or shell, write the HEALTHCHECK as an exec form command that runs something already in the image: the language runtime with a one line script, or a health subcommand built into your own binary. Exit code 0 means healthy and 1 means unhealthy.
What Docker documents
The Dockerfile reference says HEALTHCHECK tells Docker how to test whether a container is still working, for example to detect a web server stuck in an infinite loop that still has a running process. A container with a healthcheck has a health status of starting, healthy or unhealthy. The command's exit status sets it: 0 for healthy, 1 for unhealthy, and 2 is reserved.
The defaults for the options are an interval of 30 seconds, a timeout of 30 seconds, a start period of 0 seconds and 3 retries. If a run exceeds the timeout, the check counts as failed and the process is stopped with SIGKILL. Only the last HEALTHCHECK in a Dockerfile takes effect, and output on stdout or stderr is stored, with only the first 4096 bytes kept.
The command can be a shell command or an exec array. In an image with no shell, only the exec array works.
Examples by language
Node.js, using the built in fetch available in current versions:
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD ["node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
Python, using only the standard library:
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD ["python", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=2).status == 200 else 1)"]
Go, with a subcommand in your own binary, which suits a static image:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/app", "healthcheck"]
Your main function would handle the healthcheck argument by calling the local endpoint and exiting with 0 or 1.
What a good health endpoint checks
| Check | Include | Avoid |
|---|---|---|
| Process responds to HTTP | Yes | Nothing to avoid |
| Can reach its own database | Maybe, keep it cheap | Heavy queries every 30 seconds |
| Calls third party services | No | Your container looks dead when a vendor is down |
| Reveals version or config | No | Information for an attacker |
Bind the health route to localhost behaviour where possible, and return a bare status. A health endpoint that prints build details or dependency versions helps an attacker plan.
A security note on health checks
The check runs inside the container with the container's privileges. Keep it small. A check that installs a tool at run time or fetches a script from the internet adds risk for little benefit. If you are on Kubernetes, remember that it ignores the Docker HEALTHCHECK instruction and uses its own probes, so define liveness and readiness probes there as well. Verify this against your platform documentation, since runtimes differ.
Where Safeguard fits
Health checks tell you the process is alive, not that its dependencies are safe. Safeguard covers the dependency side with scanning, SBOMs and continuous monitoring that rechecks shipped builds against new advisories. Try the free scan.
Quick check
Why does my HEALTHCHECK fail with a not found error on a distroless image?
Shell form needs a shell. Use the exec array form, and use a program that exists in the image.
Does a healthy status mean the app is secure?
No. It only means the check command exited with 0.
Sources
Last reviewed: 19 October 2026.