Docker Base Image Pin Check vs. Hadolint
Related tools, different jobs. Hadolint is a broad Dockerfile linter whose own tagging rules stop at "has an explicit, non-latest tag" -- so node:18 passes it clean. This page checks the next question directly: is that tag a content digest, free, in your browser, with no install.
What each tool is for
Hadolint (github.com/hadolint/hadolint) is an open-source Dockerfile linter
covering dozens of rules -- its own DL-prefixed rule set plus ShellCheck applied to every RUN
instruction -- across style, correctness, and best practices for an entire Dockerfile. It runs as a CLI, a
Docker image, or a pre-commit hook, typically wired into CI. Two of its rules touch base-image tagging: by
its own rule documentation, DL3006 ("Always tag the version of an image explicitly") flags
a FROM line with no tag at all, and DL3007 ("Using latest is prone to errors
if the image will ever update. Pin the version explicitly to a release tag") flags an explicit
:latest tag. Neither rule requires a digest.
This checker does one narrower thing: you paste the text of a Dockerfile, and it lists
every FROM line whose base image is not pinned to a content digest -- file, line, the mutable
reference it found, and the exact docker buildx imagetools inspect command to look up the
digest yourself. A line like FROM node:18 satisfies both of Hadolint's tagging rules and passes
clean, while still being a mutable reference this tool flags: the publisher can push a new build to the
18 tag at any time, with nothing in the Dockerfile changing to show it. It runs entirely in
your browser, needs no install, and makes no network call of its own.
| Question | Where to answer it |
|---|---|
| Does my Dockerfile have shell-scripting mistakes in a RUN instruction, unnecessary layers, or other style/correctness issues across the whole file? | Hadolint -- that is its entire job, and this tool does not attempt any of it. |
| Which exact FROM lines reference a base image by tag instead of digest, and what do I run to fix each one? | This checker -- a file:line list with a copy-pasteable remediation command, not a rule-violation count. |
| My FROM line has an explicit, specific version tag like :18 -- is that enough? | No, and only this checker says so directly: DL3006/DL3007 are both satisfied by an explicit non-latest tag, so Hadolint reports it clean. |
| I want this wired into CI to block a merge automatically. | Either: Hadolint's own CI integration for its broad rule set, or this tool's check.py --fail-on-risk for the digest-pinning question specifically. |
The two are complementary, not competing: a repository can run Hadolint for the broad linting pass it is built for and still reach for this tool for the one question Hadolint's own tagging rules stop short of.
Common questions
Should I use this checker or Hadolint?
They are different shapes of tool for a related but different question. Hadolint is a broad Dockerfile linter -- dozens of rules (its own DL-prefixed rule set plus ShellCheck applied to every RUN instruction) covering style, correctness, and best practices across an entire Dockerfile, run as a CLI, a Docker image, or a pre-commit hook. This tool checks exactly one thing: whether each FROM line's base image is pinned to a content digest, and instead of a rule-violation count it lists every unpinned reference individually with the exact docker buildx imagetools inspect command to look up the digest. If you want broad Dockerfile linting -- shell-scripting mistakes in RUN, missing WORKDIR, unnecessary layers, and more -- Hadolint is the right tool and this one does not attempt any of that. If you want the specific answer to "which FROM lines are not digest-pinned, and what do I run to fix each one," this is the narrower tool for that question.
Doesn't Hadolint already flag an unpinned base image?
Partially, and this is the exact gap this tool exists to close. Hadolint's own rule documentation describes DL3006 ("Always tag the version of an image explicitly") as flagging a FROM line with no tag at all, and DL3007 ("Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag") as flagging an explicit :latest tag. Neither rule requires a digest. A line like FROM node:18 satisfies both -- it has an explicit, non-latest tag -- and passes Hadolint clean by design, while still being a mutable reference: the publisher can push a new build to the 18 tag at any time, with nothing in your Dockerfile changing. This tool flags exactly that case, which Hadolint's own documented rules do not cover.
Why would I need a separate checker if Hadolint already exists?
Two reasons. First, the coverage gap above: Hadolint's tagging rules stop at "has an explicit, non-latest tag," and this tool's entire reason to exist is the next step, "is that tag a content digest." Second, Hadolint is a Haskell binary (or a Docker image) you install and run; this tool is also available as a page you open with nothing to install, for a quick check on a Dockerfile you have not wired into a linter yet. Neither point is a criticism of Hadolint's own scope -- its rule set is deliberately broad and well beyond base-image pinning, which is exactly why a narrower, purpose-built check for this one question is worth having alongside it.
Is this page affiliated with Hadolint?
No. This site is not affiliated with, endorsed by, or sponsored by the Hadolint project or its maintainers. Hadolint is an open-source Dockerfile linter, named here only to describe what this checker is and is not, using facts drawn from Hadolint's own public rule documentation and source repository at github.com/hadolint/hadolint.