skip to content

How does pinning a Dockerfile's `FROM` to an image digest change a multi-platform build?

level: middleimportance: nice to knowfreq 22%

answer

  1. A tag can point at a list
  2. Two different digests, same tag
  3. One of them names a single architecture
  4. Pinning also freezes freshness
  5. Inspect the manifests before pasting a digest

basics

~20 s

A multi-platform tag resolves to an index listing one manifest per architecture. Pinning the index digest keeps every platform buildable; pinning one architecture's manifest digest hard-codes that architecture, and a build requested for another platform finds no matching entry.

solid answer

~40 s

`FROM debian:12-slim` is resolved through an image index (a manifest list) that maps each platform to its own manifest. If you pin `FROM debian:12-slim@sha256:...` using the **index** digest, the builder still selects the entry matching the target platform, so `--platform linux/arm64` keeps working. If the digest you pasted is a per-architecture manifest digest — the kind you get from `docker buildx imagetools inspect` when you read the platform entries — you have pinned one architecture, and a build for any other fails because the pinned manifest has no entry for it. The second effect of any digest pin is on freshness: `docker build --pull` can no longer bring anything new, so a base refresh has to arrive as an edit to that digest, normally an automated update pull request that CI builds and tests.

go deeper

for a junior

Recall that FROM image@sha256:... pins the exact bytes of the base, and that once pinned, nothing refreshes it until someone edits the Dockerfile. Know that a tag can cover several architectures.

for a middle

Explain the difference between an image index digest and a per-platform manifest digest, what each does to a build targeted at another architecture, and why a digest pin makes --pull irrelevant for freshness.

for a senior

Diagnose the symptom: a pinned base that builds on one architecture and fails on another. Know to inspect the manifest list before trusting a pasted digest, and to check every stage's pin when an advisory lands.

for a principal

Decide the estate-wide convention — tag plus index digest, automation that moves it, and a check that multi-architecture builds are covered — so that pinning delivers reproducibility without silently becoming a freeze nobody owns.

## What a tag resolves to For a multi-platform image, the tag does not point at an image; it points at an **image index** (also called a manifest list). The index is a small document listing, for each supported platform, the digest of that platform's own image manifest — `linux/amd64` here, `linux/arm64` there — together with its architecture and OS. A client asking for the tag fetches the index and then picks the entry matching the platform it wants. So two very different digests are in play for the same tag: the digest **of the index**, and the digest of **one platform's manifest**. They are both valid references and both accepted after an `@` in a `FROM` line, and they behave completely differently. ## Pinning the index digest ```dockerfile FROM debian:12-slim@sha256:<index digest> ``` The reference is exact, but platform selection still happens: the builder reads that index and takes the entry for the platform it is building for. A cross-platform build — the same Dockerfile built for `linux/amd64` and `linux/arm64` — works unchanged. This is what you want almost always, and it is what you get if the digest came from resolving the tag normally. ## Pinning a per-platform manifest digest If the digest you copied names one architecture's manifest, the `FROM` no longer has any platforms to choose between. Building for that architecture works. Building for any other fails: the builder is asked for a platform the pinned manifest does not describe and reports that it cannot find a match. The failure is confusing precisely because the Dockerfile looks pinned and correct, and because the build that produced the digest — on the maintainer's own laptop, for their own architecture — worked fine. It typically shows up the first time someone adds a second architecture to a release, long after the pin was introduced. `docker buildx imagetools inspect debian:12-slim` prints the index and the per-platform manifests underneath it, and is the way to see which of the two you are looking at before pasting one into a Dockerfile. ## The other consequence: freshness is now yours A digest is content-addressed. `docker build --pull` cannot make a digest-pinned base newer — it can only fetch those bytes if they are missing and verify they are what the digest says. That is the entire point: the pin removes the invisible, moving input. The cost is that the base can only move when something edits the Dockerfile, so a digest-pinned estate needs an automated update that opens a change bumping the digest, and CI to build and test it. Without that, the pin quietly becomes a freeze, and the freeze is the thing an image scanner will be shouting about six months later. ## Keeping both properties The usual form keeps the tag next to the digest: ```dockerfile FROM debian:12-slim@sha256:<index digest> ``` The tag is human-readable documentation of what the digest is supposed to be; the digest is what actually resolves. A bump then reads as a one-line diff that a reviewer can understand, and the same pattern works for every stage of a multi-stage build — remember that a build tool stage and a runtime stage have separate `FROM` lines, each with its own pin, and both need bumping. For a payments reconciliation batch shipped as a C++ daemon, the runtime stage is the one carrying the shared libraries an OS advisory will name, and it is the pin people most often forget to move because the build stage is the one they edit. ## Practical guidance Pin the index digest, keep the tag alongside it, let automation move it, and check on the first multi-architecture release that every platform still builds. If a pinned base suddenly refuses to build for a newly added architecture, suspect the digest before you suspect the builder. ## Why the two digests get confused Both are 64-character hex strings introduced by `sha256:`, both are accepted after an `@` in a `FROM` line, and both build successfully on the machine of whoever chose one. Nothing in the reference itself says which kind it is. The only reliable habit is to read the reference back before committing it: an index lists platforms, a manifest lists layers. Treat a digest someone pasted from a terminal transcript as unverified until you have checked which of the two it is, and prefer taking it from the tool that resolved the tag rather than from a per-platform entry.

  • Why is a tag usually kept alongside the digest in a pinned FROM line?
    Only the digest resolves; the tag is documentation. It tells a reviewer which base and which variant the digest is supposed to be, so a bump reads as an understandable one-line change rather than one opaque hash replacing another. It also keeps the reference searchable across a repository when you need to find every service on a given base.
  • In a multi-stage Dockerfile, which FROM pins actually matter for OS-package findings?
    The one behind the final stage, because that is what ships. Builder stages contribute nothing to the shipped filesystem unless something is copied out of them, so their packages are not in the image a scanner sees. They still deserve pinning for build reproducibility, but the runtime stage's pin is the one an advisory forces you to move.
  • If a digest cannot change, why do people say a digest-pinned image can still 'go stale'?
    Because the bytes are frozen while the world moves. The vulnerability data about those exact bytes keeps growing, so an unchanged, perfectly pinned base accumulates known findings over time. The pin guarantees integrity, not freshness; the two are separate properties and a digest-pinned estate needs an explicit mechanism to supply the second.

saying these in an interview costs you the question

  • Thinks a tag always names a single image manifest
  • Pastes an architecture-specific digest into a shared Dockerfile
  • Believes --pull can refresh a digest-pinned base
  • Assumes a registry can retarget an existing digest
  • Pins the builder stage but forgets the runtime stage
  • Treats a digest pin as protection against new CVEs

context