Skip to content
gohacks.dev
DevOps

Dockerfile Linter

Check a Dockerfile for unpinned images, root users, shell-form CMD, apt-get without cleanup, secrets in ENV, ADD misuse and more, with hadolint rule codes.

Free · No sign-up · Your inputs stay in your browser

Input

Lint a Dockerfile

Output

4 errors, 10 other

LineRuleFinding
1DL3007 warning:latest changes under you. Pin a version, or a digest for reproducible builds.
2DL4000 errorMAINTAINER is deprecated. Use LABEL org.opencontainers.image.authors="...".
3SEC001 errorA secret in ENV is baked into the image and visible to anyone who can pull it (docker history). Use RUN --mount=type=secret or runtime environment variables.
5DL3059 infoConsecutive RUN instructions. Combining related ones saves layers, especially when a later one deletes what an earlier one downloaded.
5DL3014 warningapt-get install needs -y, or the build stops at the prompt.
5DL3015 infoAdd --no-install-recommends to skip packages you did not ask for.
5DL3009 infoDelete /var/lib/apt/lists/* in the same RUN, or the package index stays in the layer.
6DL3059 infoConsecutive RUN instructions. Combining related ones saves layers, especially when a later one deletes what an earlier one downloaded.
6DL3003 warningUse WORKDIR to change directory; a cd only lasts for this RUN.
6NPM001 infonpm ci installs exactly what package-lock.json says and fails if it is out of date; prefer it in images.
7DL3020 errorUse COPY for files and folders. ADD also fetches URLs and unpacks archives, which is surprising when you did not mean it.
8DL3000 errorUse an absolute WORKDIR.
10DL3025 warningUse the JSON (exec) form for CMD, ["executable", "arg"]. The shell form runs under /bin/sh -c, which does not forward SIGTERM, so docker stop waits 10 seconds and kills it.
10DL3002 warningThe final stage runs as root. Add a USER with a non-root user before CMD.

Rule codes match hadolint where one exists (DL...), so a finding can be looked up or silenced the same way; SEC and NPM rules are ours. Run hadolint in CI as well: it also lints the shell inside RUN with ShellCheck, which this does not.

Runs in this browser. Nothing you paste is sent to a server.

The findings that matter most

An unpinned or :latest base image makes builds unreproducible. A final stage running as root turns a container escape into root on the host. A secret in ENV or ARG is stored in the image history for anyone who can pull it. A shell-form CMD runs under /bin/sh, which does not forward SIGTERM, so docker stop waits ten seconds and kills the process without a clean shutdown.

Run hadolint in CI

Rule codes match hadolint where one exists, so findings can be looked up and silenced the same way. hadolint also runs ShellCheck on every RUN instruction, which this browser check does not. The Docker documentation's building best practices explains the reasoning behind most rules.

Share

Frequently asked questions

Why is 'apt-get update' on its own line a problem?
The layer it creates is cached. A later apt-get install can then use an outdated package index from that cache. Run update and install in the same RUN.
How do I pass a secret to a build safely?
Use a BuildKit secret mount: RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci, and docker build --secret id=npmrc,src=.npmrc. The secret is never written to a layer.
Is what I paste sent anywhere?
No. The tool runs entirely in your browser with JavaScript. Nothing you type or paste is sent to our server or stored.

Keep planning

Similar tools