Skip to content

fix(build): use image content digest, not attested index digest#13946

Open
nabsei wants to merge 1 commit into
docker:mainfrom
nabsei:13636-stable-image-digest
Open

fix(build): use image content digest, not attested index digest#13946
nabsei wants to merge 1 commit into
docker:mainfrom
nabsei:13636-stable-image-digest

Conversation

@nabsei

@nabsei nabsei commented Jul 16, 2026

Copy link
Copy Markdown

What I did

docker compose build (via Bake) recorded BuildKit's top-level containerimage.digest metadata field as the built image's identity, used to decide whether a container needs to be recreated. With provenance attestations enabled (the default since recent Buildx/BuildKit) and the containerd image store, this digest is for the whole attested index (image manifest + attestation manifest), and it changes on every build — even a fully cached one — because the attestation manifest embeds per-build metadata (timestamps, build refs). The image content itself never changes.

This made docker compose up --build recreate containers unnecessarily on every run, even when nothing actually changed.

The fix inspects the built (or already-local) image via the Engine API with manifests=true (API 1.48+ / Engine 28.0+) and picks the digest of the manifest with Kind == "image", ignoring the "attestation" manifest. That digest only reflects the actual image content (config + layers), so it's stable across rebuilds with unchanged content, and still changes correctly when the content does change. I applied the same fix to getImageSummaries (used for already-local images) so both sides of the staleness comparison use the same, consistent digest. Images that only exist in a registry (push / multi-platform builds) aren't locally inspectable, so they keep the Bake-reported digest as before — this only affects the common local build case where the bug was reported.

Verification

I reproduced the bug against a real Docker Desktop engine (containerd snapshotter, default provenance attestations on arm64), confirmed the root cause by inspecting BuildKit's raw Bake metadata and the daemon's manifests=true image inspect response directly, then verified the fix end-to-end:

  • Before the fix: docker buildx bake on an unchanged Dockerfile produces a different containerimage.digest/image ID on every run despite CACHED build steps.
  • With the fix: two consecutive docker compose up -d --build runs with no changes keep the exact same container (same ID, same creation timestamp) — no unnecessary recreate.
  • Sanity check: changing the Dockerfile content still correctly triggers Recreate/Recreated.
  • go build ./..., go vet ./..., and golangci-lint run are all clean.
  • Full go test ./... passes; the only pre-existing failures (in pkg/watch and pkg/e2e) are environmental/flaky and reproduce identically on unmodified main, unrelated to this change.
  • Added unit tests (TestContentDigest, TestGetImageSummariesUsesContentDigestNotAttestedIndexDigest) covering the digest-selection logic directly.

Related issue

Fixes #13636


Disclosure: this PR was prepared with the assistance of Claude Code (Anthropic). All investigation, the root-cause diagnosis, the fix, and the verification steps above were done and checked by me; I'm disclosing the tool's involvement for transparency.

docker compose build (via Bake) recorded BuildKit's top-level
"containerimage.digest" as the built image's identity. With
provenance attestations enabled (the default) and the containerd
image store, that digest covers the attestation manifest as well as
the image manifest, and changes on every build even when the
runnable image content is unchanged. This made compose recreate
containers on every `up --build`, even with a fully cached build.

Instead, inspect the built (or already local) image with the
manifests API (Engine >= 28.0 / API 1.48) and use the digest of the
"image" kind manifest, which reflects only the actual image content.
Images that only exist in a registry (push/multi-platform builds)
keep the Bake-reported digest, since they aren't inspectable locally.

Fixes docker#13636

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: nabsei <nsahrane@hotmail.com>
@nabsei
nabsei requested review from a team as code owners July 16, 2026 21:07
@nabsei
nabsei requested review from glours and ndeloof July 16, 2026 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] docker compose build produces different image IDs on each rebuild with containerd image store, but works correctly with overlay2

1 participant