Dockerfile linter

Paste a Dockerfile and get findings that say what to change and why, each tied to Docker's own documentation. The file stays in this browser, and secrets in it are masked.

Stays in your browser. What you paste or choose is read on this page only. It is not sent to a server, not saved in the link or in browser storage, and never part of analytics.

Paste the text, or choose a file from your computer. Up to 256 KiB.

0 of 256 KiB

The linter sees only the Dockerfile, so this decides how COPY . is reported.

What it does

It reads a Dockerfile the way BuildKit does, stages, heredocs and line continuations included, and checks it against twelve rules taken from Docker's own best practices and build checks: who the container runs as, how base images are pinned, where secrets end up, what is downloaded during the build and how, how apt is used, how many layers are made, what COPY . sends, whether the image has a health check, and whether the main process gets stop signals. Every finding names its rule, its line, why it matters and how to fix it.

How it works

  1. The file is read on this page, up to 256 KiB, 10,000 lines and 2,000 instructions. Nothing is built, pulled or run, and no URL in the file is opened.
  2. Parser directives, comments, line continuations, heredocs, the exec and shell forms and the ARGs before FROM are read as Docker reads them. A line Docker would refuse stops the check with its line number.
  3. Each rule looks at the final stage where only the final image matters (user, health check) and at every stage otherwise. Base images are read after the global ARGs are substituted.
  4. Values that look like secrets are masked before they are shown, and the result is the same every time for the same file.

Example

A small Node.js Dockerfile with common problems, checked by the linter:

Input
Dockerfile
FROM node:latest
WORKDIR /app
COPY . .
RUN npm ci
ENV API_TOKEN=tok-8f2b61c94d0e
EXPOSE 3000
CMD npm start

Its findings:

Findings

Error0
High2
Medium1
Low2
Info1

6 findings: 0 error, 2 high, 1 medium, 2 low, 1 info.

High2

  1. HighAQ-DF-001Line 1

    The final stage (from node:latest) has no USER, so the container runs as root unless the base image sets another user.

    Fix: Create an unprivileged user (or use one the image has, such as node), give it only the paths it writes to, and add USER before CMD.

    Source

  2. HighAQ-DF-004Line 5

    ENV API_TOKEN sets a secret in the Dockerfile itself (to•••): anyone with the file or the image can read it.

    Fix: Remove the value, rotate it if it was ever committed, and pass it at build time with a secret mount (RUN --mount=type=secret) or at run time as an environment variable or a file.

    Source

Medium1

  1. MediumAQ-DF-002Line 1

    FROM node:latest follows latest.

    Fix: Pin a version tag (python:3.13-slim), or a digest (@sha256:…) for builds that must be reproducible.

    Source

Low2

  1. LowAQ-DF-009Line 3

    COPY . copies the whole build context. Make sure a .dockerignore excludes .git, .env and build output.

    Fix: Add a .dockerignore next to the Dockerfile (at least .git, .env*, node_modules, build output), or copy only the files the image needs.

    Source

  2. LowAQ-DF-011Line 7

    CMD is in shell form, so the process does not receive stop signals.

    Fix: Use the exec form, a JSON array: CMD ["node", "server.js"]. If you need the shell, end the script with exec.

    Source

Info1

  1. InfoAQ-DF-010Line 1

    The final image has no HEALTHCHECK of its own.

    Fix: Add a HEALTHCHECK that calls a cheap endpoint with a tool the image has (wget -qO-, curl -f), or HEALTHCHECK NONE if health is checked elsewhere.

    Source

Limits

  • A static check: the base image is not pulled, so the linter does not know which user it sets, whether it has a health check, or what its entrypoint does.
  • Official server images such as PostgreSQL, Redis, nginx and httpd start as root on purpose and drop to their own user; the root-user rule reports them anyway. The rules page lists the known false positives found on official Dockerfiles.
  • Only the Dockerfile is read: .dockerignore, the build context and scripts that RUN calls are not.
  • Not a vulnerability scan: it does not list packages or CVEs in the image.

What each rule checks, with examples

Start from a Dockerfile that passes: the Dockerfile generator

Methodology: how the numbers are worked out

Sources

  1. Docker Docs: Dockerfile reference
  2. Docker Docs: Building best practices
  3. Docker Docs: Build checks
  4. Docker Docs: Build secrets
  5. Docker Docs: Build context and .dockerignore

Updated

Next step

Docker Compose Analyzer

A free public tool by ANAROQ, a company building compute infrastructure. What ANAROQ is building