Chris Collins

Tools · containers

Dockerfile explainer and linter

Paste a Dockerfile and see what every instruction does, which ones make layers, what a change rebuilds, how the stages connect and roughly how big the image is — with hadolint-style lints and one-click fixes.

Added

How to use it

Paste or type a Dockerfile, or start from a preset: Node, Python, Go (multi-stage), Java, heredocs and secrets, or a deliberately bad one. Everything updates as you type, and nothing leaves the page. Lines with errors or warnings are tinted in the editor.

How to use it

  • Explain — first, what the container will actually run: the final stage’s ENTRYPOINT and CMD combined the way the Dockerfile reference’s table says (shell form becomes /bin/sh -c …; a shell-form ENTRYPOINT swallows CMD; setting ENTRYPOINT resets an inherited CMD). Type arguments for docker run IMAGE … to see them replace CMD. Then a card per instruction, by stage: whether it makes a filesystem layer or only changes metadata, what its flags do (--mount=type=cache|secret|ssh|bind, --from, --chown, --link, --platform), what a heredoc runs, which ARGs and ENVs are in scope (an ARG before the first FROM is visible only to FROM lines), and the parser directives (# syntax=, # escape=, # check=). The L-number button jumps to the line in the editor.

  • Lint — findings with the rule ids of hadolint (DL3006 untagged base image, DL3007 :latest, DL3008/3013/3016/3018 unpinned packages, DL3009 apt lists left behind, DL3014 -y, DL3015 --no-install-recommends, DL3020 ADD for local files, DL3025 shell-form CMD, DL3003 cd, DL3004 sudo, DL4006 pipes without pipefail, DL3002 root user, DL3022–3024 stage references, …) and of Docker’s own build checks (SecretsUsedInArgOrEnv, UndefinedVar, FromAsCasing, LegacyKeyValueFormat, …), plus a few from Docker’s best practices (an apt-get update alone in a RUN, copying the whole context before installing dependencies, no USER). Many have a Fix button that edits the Dockerfile; Apply all applies them one at a time.

  • Layers & cache — the final image as a stack: the base image at the bottom, a bar per layer with a rough size, and metadata instructions as dashed lines (they add no layer). Below it, the cache explorer: tick the files you changed (or type paths), the build args you changed, "base image updated", lines you edited, or --no-cache, and every step says whether it comes from the cache, rebuilds, or depends on whether an earlier stage’s output changed — with the reason. Edit the .dockerignore to see ignored files stop mattering. When a dependency install re-runs only because COPY . . came first, it says how to reorder.

  • Stages — the multi-stage graph: FROM another stage, COPY --from and RUN --mount=from, with the target stage marked and the stages BuildKit skips faded. Choose another --target to see what it would build.

The cache rules modelled are Docker’s: a step’s key is its parent plus the instruction; COPY and ADD hash the matched files' contents (not timestamps); RUN compares only its command text; a changed build arg misses at the first RUN after its ARG; COPY --from checksums what it copies; the first miss rebuilds everything after it in that stage (Docker: build cache invalidation).

Limits

  • The size estimate is a ballpark: base images come from a small table of approximate uncompressed linux/amd64 sizes for recent tags (shown under the stack, labelled approximate; Docker Hub shows compressed sizes, about ⅓–½ of these), and package and dependency sizes are typical guesses. docker history and docker images give the real numbers.

  • Base images are not fetched: their ENV, USER, ENTRYPOINT and CMD are unknown, so "no USER" and "nothing to run" may be satisfied by the base image.

  • RUN commands are read with a small shell reader, enough to find apt-get, apk, pip, npm, yarn, cd, sudo and pipes — not a full shell parser. hadolint itself also runs ShellCheck, which this doesn’t.

  • The cache model is BuildKit’s in outline; ADD of URLs and git sources, COPY --link rebasing and remote cache backends are simplified.

Linking to an example

Share link copies a link to the Dockerfile as you have it — a preset by name, otherwise the text packed into ?s=. Values of ENV and ARG variables that look like secrets (PASSWORD, TOKEN, SECRET, API keys) are replaced by * first. The tab goes after #: explain, lint, layers or stages.

Parameter Meaning Example

preset

One of the presets below

preset=bad

changed

Files changed in the cache explorer, comma-separated

changed=src/server.ts

preset= Shows

node

Node.js in three stages: production dependencies, a build stage, and a slim runtime with a healthcheck and USER node

python

Python slim with apt done right, pip --no-cache-dir, and a non-root UID

go

Go cross-compiled with --platform=$BUILDPLATFORM and cache mounts into distroless, plus a test stage the target skips

java

Maven with a dependency cache, then a JRE on Alpine; ENTRYPOINT plus CMD as default arguments

heredoc

RUN <<EOF and COPY <<EOF, a secret mount, SHELL with pipefail, ONBUILD

bad

Most of the classic mistakes at once — try Apply all fixes

For example ?preset=node&changed=src/server.ts#layers shows which steps a source change rebuilds.

Runs entirely in your browser; nothing you type leaves this page.