The Dockerfile Linter checks a pasted Dockerfile against widely-used best-practice and security rules — the same kinds of issues hadolint flags — and explains each finding so you can fix it. Everything runs in your browser.
How it works
The linter first tokenises the Dockerfile into instructions, joining lines that end with a backslash continuation and skipping comment lines. It then applies a rule set to each instruction and to the file as a whole:
- Base image pinning — warns on untagged images and on the
latesttag, which is not reproducible. - Package manager hygiene — flags
apt-get installwithout--no-install-recommends,apt-get updatein its own layer, missing apt list cleanup, andapk addwithout--no-cache. - Layer and path correctness —
cdinside RUN (use WORKDIR), relative WORKDIR paths, andADDwhereCOPYis the safer choice. - Security — running as
rootat the final stage, use ofsudo, and piping acurl/wgetdownload straight into a shell withoutpipefail. - Runtime correctness — shell-form
CMD/ENTRYPOINT(use the JSON exec form so signals are forwarded) and a missingHEALTHCHECK.
Before and after examples
Unpinned base image:
# Before — flagged
FROM node:latest
# After — pinned
FROM node:22-slim
apt-get hygiene:
# Before — three issues: update alone, no --no-install-recommends, no list cleanup
RUN apt-get update
RUN apt-get install curl git
# After — all three fixed in one layer
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
Shell-form CMD (loses signal forwarding):
# Before — shell form wraps CMD in /bin/sh -c; SIGTERM hits sh, not your app
CMD node server.js
# After — exec form; signals reach the process directly
CMD ["node", "server.js"]
Running as root:
# Before — process has root inside the container
COPY . .
CMD ["node", "server.js"]
# After — switch to the built-in non-root user before CMD
COPY . .
USER node
CMD ["node", "server.js"]
Why shell-form CMD matters for signal handling
When you run CMD node server.js, the shell interprets it as /bin/sh -c "node server.js". Docker sends SIGTERM to PID 1 when stopping a container — that is the shell process, which may not forward the signal to your app. Your Node server then receives a SIGKILL after the stop timeout and cannot do graceful shutdown. The exec form CMD ["node", "server.js"] makes your app PID 1, so it receives SIGTERM directly.
Notes
Severities are advisory: error marks things that are almost always wrong,
warning marks strong best practices, and info marks size or hardening
optimisations. This linter is a quick preview — keep hadolint in CI for full
rule coverage and its shellcheck integration for complex RUN scripts.