Three layers, and a lockfile covers one
The question that tests whether a build is reproducible is simple: does rebuilding the same commit six months later produce the same artefact? For almost every project the answer is no — and the three layers below are the three reasons.
| Layer | Pinned by | Covered by a lockfile? |
|---|---|---|
| Language packages | packages.lock.json, package-lock.json | Yes |
| The base image | a sha256:... digest, not a tag | No |
| CI tooling | a commit SHA, not a tag | No |
The bottom two are the layers people skip, and they get skipped for the same reason: a tag looks like a version but it is a pointer that can move.
FROM node:20 does not say "version 20", it says "whatever image is labelled 20 right now". That label is updated with each patch release, so two builds a week apart use two different images. Usually that is good (security patches arrive automatically), and that is exactly why people leave it — but it also means you do not know what you are running, and a compromised image arrives by the same route.
uses: actions/checkout@v4 is worse from a trust standpoint: v4 is a git tag, and a git tag can be moved by anybody with push access to that action's repo. An action runs inside your workflow with the workflow's token — repo read/write and your secrets.
The trade-off has to be said out loud: pinning by digest means security patches stop arriving on their own. You trade "silent, uncontrolled updates" for "controlled updates you must initiate". That trade is only worth it if you genuinely have an update mechanism — so pinning without Dependabot or Renovate makes the system worse, not better.
Comments
Commenting needs an account with at least one completed lesson. That condition is what keeps this thread worth reading: every point belongs to someone who can be asked back, and reputation accrues over time.
You can still read every comment below without an account. Signing in brings you back to this exact spot, not to the top of the page.
Loading comments…