ghtier
← IaC and container config

Hadolint

Lints Dockerfiles against best-practice rules and runs ShellCheck on the shell script in every RUN instruction.

github.com/hadolint/hadolint ↗
Stars
★ 12,466
License
GPL-3.0
Language
Haskell
Last push
25 Sept 2026
OpenSSF Scorecard
4.3 / 10
ghtier ratingRated 4 of 5, Recommended4/5 · Recommended

The standard Dockerfile linter and actively released again, but most rules are about reproducibility and hygiene, so pair it with an image scanner.

How it works

Hadolint is written in Haskell. It parses the Dockerfile into an AST with the language-docker library and runs its DL rules over the instruction list. Because rules see the whole file, some track state, such as which USER is set last.

The shell code in each RUN instruction is handed to ShellCheck, which hadolint embeds, and those findings come back as SC rules next to the DL ones. Hadolint never builds the image, pulls the base image or looks at what is installed in it.

Settings come from .hadolint.yaml (ignored rules, severity overrides, failure-threshold, trustedRegistries for DL3026, a label schema) or from CLI flags. A # hadolint ignore=DL3008,SC2086 comment covers the next instruction and # hadolint global ignore=... covers the file. Output is tty, json, checkstyle, codeclimate, gitlab_codeclimate, gnu, codacy, sonarqube, sarif or junit, and the exit code is non-zero when a finding reaches the failure threshold.

What it catches

  • FROM images with no tag or with :latest (DL3006, DL3007)
  • Unpinned package versions in apt-get, apk, pip, npm, gem and go install
  • Last USER is root, or sudo used in RUN (DL3002, DL3004)
  • ADD used where COPY would do (DL3020)
  • Base images from registries outside your trusted list (DL3026)
  • Credential-looking names in ARG or ENV (DL3064)
  • Shell bugs in RUN lines and pipes without pipefail (ShellCheck, DL4006)

Run it

brew install hadolint
hadolint Dockerfile
# or without installing:
docker run --rm -i hadolint/hadolint < Dockerfile

In GitHub Actions

hadolint:
  runs-on: ubuntu-latest
  permissions:
    contents: read
    security-events: write
  steps:
    - uses: actions/checkout@<sha> # v7.0.1
      with:
        persist-credentials: false
    - uses: hadolint/hadolint-action@<sha> # v3.5.0
      with:
        dockerfile: Dockerfile
        format: sarif
        output-file: hadolint.sarif
        no-fail: true
    - uses: github/codeql-action/upload-sarif@<sha> # v4.38.3
      with:
        sarif_file: hadolint.sarif

What leaves your machine

Nothing. It reads the Dockerfile locally and does not contact registries.

Limits

  • Only sees the Dockerfile text. It cannot tell you about CVEs or secrets baked into the base image or the built layers.
  • DL3006 and DL3007 accept any explicit tag, and no rule requires pinning FROM to a sha256 digest.
  • Release gaps have been long (v2.12.0 in 2022, then v2.13 in 2025), so check the rule list against the version you actually run.

Links

Related tools

GitHub stats as of 10 Oct 2026. How ratings work.