Zennoxa Shield

Do popular projects pin their base images? We checked 25

Popular projects SHA-pin their GitHub Actions two-thirds of the time — but pin their Docker base images to an immutable digest only 7.6% of the time. Same supply-chain idea, a fraction of the adoption. Reproducible data across 25 repos.

July 21, 2026By Zennoxa Researchsupply-chaindockercontainersdevsecops

TL;DR — Across 25 popular infrastructure and application projects (250 external base-image references), only 7.6% are pinned to an immutable @sha256 digest. 92.4% use a mutable reference. Compare that to our GitHub Actions pinning study, where 67.6% of action references were SHA-pinned: it's the exact same supply-chain idea — pin to something that can't change underneath you — adopted ~9× less often for containers than for CI actions.

The same problem, one layer down

We already looked at GitHub Actions pinning: a uses: action@v4 runs whatever that mutable tag points at today, so the hardened practice is to pin to an immutable commit SHA. A Docker base image is the identical risk one layer down. FROM node:20 (or worse, FROM node:latest) pulls whatever image that tag resolves to at build time. Tags are mutable — the same node:20 can be a different image tomorrow — so a reproducible, tamper-evident build pins to the content digest: FROM node@sha256:….

So we asked the same question of containers that we asked of Actions: how often do popular projects actually pin?

What we measured

  • Corpus: 25 popular infra/app repos; 22 ship Dockerfiles with external base images.
  • Per repo: sparse-checkout the Dockerfiles, parse every external base-image FROM (excluding internal stage aliases, --platform flags, build-args, and scratch), and classify the reference as digest (@sha256:…, immutable), version tag (node:20.1.2, mutable), or :latest/untagged (most mutable).
  • Tool: Shield's CONTAINER-002 (:latest) cross-checked against an independent FROM parser; the :latest counts agreed (16 vs 15, one build-stage edge case). Digest classification is unambiguous — a reference either contains @sha256: or it doesn't — and we hand-verified samples at both ends (posthog's python@sha256:355b… vs grafana's golang:latest).

Full per-repo dataset: data.json.

The numbers

Pin type Refs Share
Digest (@sha256, immutable) 19 7.6%
Version tag (node:20.1.2) 204 81.6%
:latest or untagged 27 10.8%
Total external base images 250 100%
  • 7.6% digest-pinned. Only 5 of 22 projects use @sha256 pinning for any base image (posthog, Ghost, elasticsearch, moby, n8n).
  • 81.6% version tags. The bulk of the ecosystem sits here — reasonable and readable, but still mutable (a tag can be repushed).
  • 10.8% :latest/untagged — the genuinely non-reproducible end (FROM golang:latest, FROM busybox).

The cross-study point

Put the two studies side by side:

Immutability practice Adoption across popular repos
GitHub Actions pinned to a commit SHA 67.6%
Docker base images pinned to a digest 7.6%

Same principle, ~9× the adoption for Actions. Our read: the GitHub Actions compromises of 2025 (tj-actions, reviewdog) put SHA-pinning on every CI checklist, and Dependabot made it a one-click habit. The base-image equivalent hasn't had its forcing function yet — and digest strings are ugly to write by hand, so most teams stop at a version tag.

A fair caveat

Version tags are not "wrong." node:20.1.2 is readable, gets patch updates, and is fine for most teams. Only a @sha256 digest is truly immutable, so we report it as the strict gold standard for reproducible/tamper-evident builds — not as a claim that everyone on a version tag is insecure. The clearly-risky category is :latest/untagged, which makes builds non-reproducible and lets the base change silently. Note too that some pinned/unpinned images are a project's own first-party images, where the trust model differs.

How to pin

# mutable — resolves at build time, can change underneath you
FROM python:3.13-slim

# immutable — the exact image content, human-readable tag kept in a comment
FROM python:3.13.13-slim-bookworm@sha256:355bfa66770995d7e9a0da4b3473b44d0cb451f6b56f5615ad9c39e3c4eca03f

Then let Dependabot (package-ecosystem: docker) or a tool like pin-github-action's container equivalent bump the digest so you stay patched without tracking a moving tag. Shield flags :latest base images (CONTAINER-002) today.

Limitations

  • 25 popular repos, point-in-time (2026-07-21) — not a random sample; large projects skew more disciplined, so the ecosystem-wide digest rate is plausibly lower.
  • We classify references, not unique images; a reused base weights a repo's count.
  • Stage aliases, --platform flags and build-args are excluded; one residual cross-file alias (~0.4%) may remain and doesn't move the headline.

Reproduce it

git clone --depth 1 --filter=blob:none --sparse https://github.com/grafana/grafana g
cd g && git sparse-checkout init --no-cone && git sparse-checkout set '**/Dockerfile' && git checkout
# a base image is immutable only if the ref contains @sha256:
grep -rhE '^\s*FROM\s' --include='*Dockerfile*' . | grep -v '@sha256:'

Per-repo counts and method in data.json.


Zennoxa Research publishes reproducible security data on public projects with public tooling. No third-party scanner is involved.

Shield flags Dockerfiles that pull a mutable base tag (:latest, :edge) instead of a pinned one — the exact gap this study measured.

See what Shield finds in your code

Free to start, no credit card. One pass covers SAST, secrets, vulnerable dependencies, containers and IaC — ranked by what’s actually reachable, not just severity.

Scan your repo free

Prefer the terminal? The offline CLI runs with no account

Published by Zennoxa Research — the evidence program behind Zennoxa Shield.How Shield ranks findingsThe numbers, reproducibleShield on GitHub
More research
Do popular projects pin their base images? We checked 25 — Zennoxa Shield