Dockerfile Linter

Paste a Dockerfile to check it against more than 40 rules. Every finding has a rule id, a severity, the line, an explanation of the consequence and a concrete fix.

Loading tool…

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

  1. Paste your Dockerfile or open the file.
  2. Read the verdict above the editors: stages, base images and the counts of errors, warnings and notes.
  3. Go through the findings, which are sorted by line; each one explains the consequence and the fix.
  4. 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?
No. It is an independent implementation with its own rule ids (DK001…) that covers the most useful checks of hadolint and of Docker's build checks. It does not run ShellCheck on the commands, and it does not check whether package versions are pinned. It needs nothing installed and sends nothing to a server.
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?
That is the shell form: Docker runs /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.
The linter only sees the Dockerfile, not the base image. Most images run as root by default, so a final stage without 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.

Related tools