About the Dockerfile Linter
This linter parses the Dockerfile the way Docker does — parser directives, comments, line continuations, heredocs, --flags and multi-stage builds — and then applies rules in the spirit of hadolint and Docker's own build checks. Errors (red) are things that break the build or the image: no FROM, an unknown instruction (often a missing backslash on the line above), an exec form that is not valid JSON such as CMD ['node', 'app.js'], an EXPOSE port above 65535, two stages with the same name, or COPY --from that points to a later stage.
Warnings concern reproducibility and security. A base image without a tag or with latest changes under you. apt-get update in its own RUN is cached, so a later install uses a stale index. apt-get install without -y waits for an answer that never comes. curl … | sh runs unverified code. A name such as DB_PASSWORD in ENV or ARG ends up in the image, readable with docker history. The shell form of CMD and ENTRYPOINT wraps the process in /bin/sh -c, so it does not receive the signal of docker stop. And a final stage without USER usually runs as root.
Notes (blue) are about image size and style: --no-install-recommends, removing /var/lib/apt/lists in the same layer, pip install --no-cache-dir, apk add --no-cache, npm ci instead of npm install, pipes without pipefail, instructions in lower case. Rules look inside RUN: the command line is split at &&, ; and |, so a finding points to the physical line of the command, not to the start of the instruction. The linter reads text only; it does not build the image or pull anything.
How to use it
- Paste your Dockerfile or open the file.
- Read the verdict above the editors: stages, base images and the counts of errors, warnings and notes.
- Go through the findings, which are sorted by line; each one explains the consequence and the fix.
- Apply the fixes and watch the list shrink as you type. “Good example” loads a Dockerfile that passes.
Frequently asked questions
Is this the same as hadolint?
Why is the latest tag a problem?
latest is just a tag that the publisher moves to every new release. The same Dockerfile can therefore produce a different image tomorrow, and a cached latest on one machine differs from a fresh pull on another. Pin a version (node:22.9.0-bookworm-slim) or a digest (@sha256:…).What is wrong with CMD node server.js?
/bin/sh -c "node server.js", so the shell is process 1 and your application is its child. The shell does not forward signals, so docker stop waits for the timeout and then kills the container. The exec form CMD ["node", "server.js"] runs the program directly.The linter says my container runs as root, but my base image sets a user.
USER gets a warning. If your base image already switches to an unprivileged user (distroless nonroot images, for example), the warning does not apply; an explicit USER documents the intent.