RousselTMLEARNING

Observabilité

Linting Dockerfiles with Hadolint and docker build --check

Spot Dockerfile errors and bad practices before building: Hadolint, its rules, thresholds, and BuildKit built-in checks.

1. Why lint a Dockerfile? #

Find problems before building.

The principle #

A linter reads the Dockerfile without building it and flags errors and bad practices: untagged image, ADD instead of COPY, apt cache kept, shell-form CMD. Hadolint is the reference; it also analyzes RUN commands with ShellCheck.

Linting at each stage: editor, pre-commit, CI, docker build --check

2. Hadolint in practice #

A real result, and how to read it.

The Dockerfile to fix #

FROM node
RUN apt-get update && apt-get install -y curl
ADD . /app
WORKDIR /app
RUN npm install
EXPOSE 3000
CMD npm start
hadolint Dockerfile
# ou sans installation
docker run --rm -i hadolint/hadolint:v2.15.1 < Dockerfile
Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly
Dockerfile:2 DL3008 warning: Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
Dockerfile:2 DL3015 info: Avoid additional packages by specifying `--no-install-recommends`
Dockerfile:2 DL3009 info: Delete the apt lists (/var/lib/apt/lists) after installing something
Dockerfile:3 DL3020 error: Use COPY instead of ADD for files and folders
Dockerfile:7 DL3025 warning: Use arguments JSON notation for CMD and ENTRYPOINT arguments
RuleProblemFix
DL3006Untagged imagenode:22.11-bookworm-slim
DL3020ADD for local filesCOPY
DL3015 / DL3009Recommended packages, apt cache kept--no-install-recommends, rm -rf /var/lib/apt/lists/*
DL3025Shell-form CMD: signals poorly forwardedCMD ["npm", "start"]

The result after fixing #

On the previous lesson's hardened Dockerfile, Hadolint still flagged DL3066 for USER nonroot: a user name may not be resolvable by the host. With USER 65532:65532, it reports nothing and returns code 0.

Thresholds and exceptions #

By default, Hadolint fails on the first finding, even at info level. In CI, set a threshold, and justify each exception in .hadolint.yaml:

hadolint --failure-threshold warning Dockerfile
hadolint -f json Dockerfile   # pour un outil de CI
# .hadolint.yaml
ignored:
  - DL3008   # apt versions managed by Renovate

Occasionally, a # hadolint ignore=DL3008 comment right above the line.

3. Built-in checks #

docker build --check.

docker build --check #

BuildKit embeds its own rules (keyword casing, inconsistent FROM … AS, undefined variables, shell-form CMD…). The command evaluates them without building the image:

docker build --check .

They complement Hadolint without replacing it: Hadolint goes further on shell commands and packages. This command was not run for this article, as our test environment had no Docker daemon.

Sources #

Real output from Hadolint 2.15.1.

Related articles

Hardened images: reducing your containers’ attack surface

Choosing a hardened base image: slim, distroless, Docker Hardened Images, Chainguard; non-root, pinned versio…

Scanning Docker images: Trivy, Grype and Docker Scout

Detect image vulnerabilities and misconfigurations with Trivy, read results, filter what is fixable and block…

SBOM: the component inventory of your images

Generate a CycloneDX or SPDX SBOM, rescan it without rebuilding and know in seconds which images a new flaw a…

Keep learning

Support us

♥ SUPPORT US

Support RousselTM Learning

Courses, articles, labs and certificates stay free with no intrusive ads. A voluntary contribution, of any amount you choose, pays for hosting, lab environments and new content.

Contribute with PayPal Any amount · card or PayPal accountLearn more
QR code PayPal
Or scan this QR code with your phone

Thank you, every contribution counts!