Dockerfile check · low

Final stage has no HEALTHCHECK

A HEALTHCHECK tells Docker how to test that the app inside a container is actually working, not just that its process exists. Without one, Docker and Compose report a hung or broken app as running, and Compose's depends_on with condition: service_healthy has nothing to wait for. Kubernetes ignores HEALTHCHECK and uses its own liveness and readiness probes.

$ explain Dockerfile
! Final stage has no HEALTHCHECK low
line 1
fix: see below
$ 

What the free Dockerfile explainer reports on the risky example below.

Why it matters

A process can stay alive while the app is deadlocked, out of connections or failing every request. Without a health check, docker ps shows it as up and nothing reacts.

With a HEALTHCHECK, Docker marks the container healthy or unhealthy. Compose can then start dependent services only once it is healthy, and Swarm replaces unhealthy tasks. Plain docker run only reports the status; it does not restart the container on its own.

HEALTHCHECK NONE disables a check inherited from the base image, so the explainer treats it the same as having none.

Risky and safer Dockerfile

RiskyDockerfile
FROM node:22.11-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
USER 10001
CMD ["node", "src/server.js"]
SaferDockerfile
FROM node:22.11-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
USER 10001
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD ["node", "src/healthcheck.js"]
CMD ["node", "src/server.js"]

How to fix it

  • Write a cheap check that exercises the app, e.g. a small script that requests a /health endpoint and exits 0 on success and 1 on failure.
  • Add it with timings that fit your app: HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 CMD ["node", "src/healthcheck.js"].
  • Use a tool that exists in the image. Slim images often lack curl or wget, so a check written in the app's own runtime avoids adding packages.
  • On Kubernetes, define liveness and readiness probes in the pod spec instead; they are what Kubernetes uses.

When it's acceptable

  • Images deployed only to Kubernetes with liveness and readiness probes, since Kubernetes ignores HEALTHCHECK.
  • Short-lived jobs and CLI images that run to completion, where there is no long-running service to check.

Check your own Dockerfile

The risky example is loaded below. Paste your own file to check it for this and every other Dockerfile rule. It runs in your browser; nothing is uploaded.

Dockerfile

Parsed in your browser. Nothing is uploaded.

Explanation

1 stage7 instructions2 low

Stage 0node:22.11-slim

  • Docs for FROM (opens in a new tab)

    Starts from the base image node, tag 22.11-slim.

    A version tag can be re-pushed to different content; only a digest is immutable. This applies to every tagged FROM in the file.

    Safer: Pin the digest as well (image:tag@sha256:<digest>) and let a bot open pull requests when it changes.

    Without one, Docker and Compose can't tell a running container from a working one, and depends_on with service_healthy has nothing to wait for. Kubernetes ignores HEALTHCHECK and uses its own probes.

    Safer: Add a HEALTHCHECK with an interval, timeout and retries, or define liveness and readiness probes in Kubernetes.

  • Docs for WORKDIR (opens in a new tab)

    Sets the working directory to /app for later instructions and the container. It is created if it does not exist.

  • Docs for COPY (opens in a new tab)

    Copies package.json, package-lock.json from your build context to ./ in the image.

  • Docs for RUN (opens in a new tab)

    Runs npm ci --omit=dev while building the image and saves the result as a new layer.

  • Docs for COPY (opens in a new tab)

    Copies src from your build context to ./src in the image.

  • Docs for USER (opens in a new tab)

    Runs later instructions and the container's process as user 10001.

  • Docs for CMD (opens in a new tab)

    Sets the default command to node src/server.js. It runs when the container starts, and anything passed to docker run replaces it.

Questions

Does Kubernetes use the Dockerfile HEALTHCHECK?

No. Kubernetes ignores it and uses the liveness, readiness and startup probes defined in the pod spec.

Does Docker restart an unhealthy container?

Plain Docker only marks the container unhealthy; a restart policy reacts to the process exiting, not to health status. Docker Swarm services do replace unhealthy tasks.

What should a Docker HEALTHCHECK command return?

Exit code 0 means healthy and 1 means unhealthy. Keep the check fast and give it a timeout so a hung app is detected rather than waited on.