Dockerfile best practices, checked line by line
A Dockerfile that works is not necessarily a good one. Images built from floating tags change under you, containers that run as root turn a small bug into a full compromise, and careless layer ordering makes builds slow and images large.
Paste a Dockerfile and this linter points out common problems with the line number and a short explanation: unpinned base images, missing USER and WORKDIR, apt caches left in the image, ADD used for local files, plain http:// downloads, shell-form commands and more.
How to use it
- Paste your Dockerfile into the box.
- Read the list of findings; each has a line number and a severity: warn or info.
- Fix the warnings first; they cover security and reproducibility.
- Consider the info items, which are style and size improvements.
Example: a quick first Dockerfile
FROM node:latest RUN apt-get update && apt-get install -y curl ADD . /app CMD npm start
1 warn from-latest-tag 1 info from-distroless-or-slim 2 warn apt-cleanup 3 info add-vs-copy 5 warn user-not-set 5 info workdir-not-set
Four lines, six findings: the image floats, keeps the apt cache, copies with ADD, and runs as root from an unpredictable working directory.
Pin, slim down and clean up
Pin the base image to a version, such as node:22.11-bookworm-slim, so a rebuild next month produces the same image; for full reproducibility pin the digest too. Slim and Alpine variants leave out compilers and tools you do not need at runtime, which reduces size and attack surface.
Install packages and clean the package cache in the same RUN instruction: RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*. Each instruction creates a layer, and files deleted in a later layer still take up space in the earlier one.
Do not run as root
Containers run as root unless told otherwise. If an attacker exploits the application, root inside the container makes escaping to the host and tampering with mounted files far easier. Create an unprivileged user and switch to it with USER before the final CMD; official images such as node already include a node user.
COPY, ADD and the command form
COPY does one predictable thing: copy files from the build context. ADD also downloads URLs and unpacks archives automatically, which can surprise you, so use it only when you want that. Write CMD and ENTRYPOINT in exec form, CMD ["node", "server.js"], so the process receives stop signals directly instead of through a shell and shuts down cleanly.
Questions
Is this a replacement for hadolint?
No. It checks a focused set of common rules in the browser. Hadolint checks many more rules and also lints the shell commands inside RUN; use it in CI.
Does it build or run my Dockerfile?
No. It only reads the text. Nothing is executed or uploaded.
Why is a missing WORKDIR only info?
Images still work without it, but paths become relative to /, which makes later instructions and debugging harder to follow.