Skip to content

feat: resolve digest-pinned Docker Hub images in WITH DOCKER locally - #1008

Draft
kmannislands wants to merge 4 commits into
mainfrom
digest-pinned-local-resolution
Draft

kmannislands wants to merge 4 commits into
mainfrom
digest-pinned-local-resolution

Conversation

@kmannislands

Copy link
Copy Markdown

Summary

Stacked on #513 (base: giles-compose-digest-fix). Depends on EarthBuild/buildkit#43.

#513 makes digest-pinned images work in WITH DOCKER by retagging them without the digest. The pin is still enforced, but the daemon has to fetch the pinned manifest from the network, so builds aren't offline-capable. Private images also fail, since dind has no registry credentials. This makes digest-pinned Docker Hub images resolve from BuildKit instead:

  • BuildKit (feat(earthlyoutputs): serve original content of digest-pinned images buildkit#43) serves the original index, manifest and config of the image an export was pulled from, when the export carries export-image-local-registry-source. The content comes from its content store, where it stays leased to the pulled image, so no network is needed once the image is cached.
  • dockerd-wrapper.sh makes the embedded registry the first Docker Hub mirror of the WITH DOCKER daemon. Unknown content is a 404, and dockerd moves on to the next mirror, then Docker Hub.
  • earthfile2llb: Docker Hub pulls pinned by digest are marked with their source ref and not retagged. The daemon pulls them by digest from the mirror when compose or docker run uses them. They no longer take part in tag collisions, so fix: enable digests in compose files #513's collision guard only applies to the retag path.
  • Bumps go.mod and BUILDKIT_BASE_IMAGE to the buildkit PR branch. Repoint both to the merge commit before merging.

Falls back to #513's behaviour when:

  • buildkitd lacks the earthly.localregistry.source capability, or
  • the image isn't on Docker Hub. dockerd mirrors only apply to Docker Hub; other registries need the containerd image store, which is blocked by Enable containerd snapshotter for dind #195.

Testing

New tests/with-docker-compose+test-digest-pinned-image-offline (added to +all):

  • It cuts the WITH DOCKER daemon off from the network via daemon.json proxies, pointed at a dead port with the embedded registry exempt. I tried iptables first, but BuildKit reuses network namespaces across RUNs, so firewall rules leak into later builds.
  • It then runs name:tag@digest and name@digest compose services, asserts docker image inspect alpine@sha256:…, and finishes with a control docker pull busybox:1.37 that must fail, proving the daemon really was offline.

Results with a fresh CLI and buildkitd from this branch:

  • A/B: the offline test passes here and fails on fix: enable digests in compose files #513's head with proxyconnect … connection refused.
  • tests/with-docker-compose+all, tests/local/with-docker-compose-local-reg+all, the AST test and earth +lint pass.

Limitations

  • Docker Hub only (see above).
  • The pinned image must already be available to BuildKit (cached, or reachable through BuildKit's own registry config) for a fully offline run.

🤖 Generated with Claude Code

gilescope and others added 4 commits October 6, 2026 19:37
`docker tag` refuses digest-bearing targets, so a compose service pinned
by `image: name[:tag]@sha256:...` (or `WITH DOCKER --pull name@sha256:...`)
failed the image pre-pull with "refusing to create a tag with a digest
reference".

Strip the digest from the retag target in both the registry and local
registry paths. The pin is still enforced: compose resolves the image by
digest, which the retagged image cannot satisfy, so it fetches the manifest
upstream and verifies it, reusing the pre-pulled layers.

Fixes #512

Signed-off-by: Giles Cope <gilescope@gmail.com>
Co-authored-by: Kieran Mann <kieran.mann@o8t.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LOCALLY leaves compose pulls to the host's compose, so this does not
exercise the retag fix, but guards that digest pins keep working there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the digest stripped, a pinned image is retagged under its tag-only
name. If another image in the same WITH DOCKER claims that name (e.g.
`alpine@sha256:...` next to `alpine`), the parallel retags race and either
image may end up owning the tag.

Skip pre-pulling the pinned image in that case and warn. Compose resolves
pinned images by digest, which a retag cannot satisfy, so it pulls the
image itself and nothing is lost but layer reuse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A digest-pinned image (e.g. compose `image: alpine@sha256:...`) could only
be retagged into the WITH DOCKER daemon: the embedded registry served a
rebuilt image whose digest differs from the pin, so the daemon still had
to fetch the pinned manifest from Docker Hub. That needs the network, and
credentials the daemon does not have for private images.

BuildKit can now serve the original content of the image an export was
pulled from (earthbuild/buildkit `earthly.localregistry.source`). Use it:

- Mark WITH DOCKER pulls of Docker Hub images pinned by digest with their
  source reference, so the embedded registry also serves their original
  index, manifest and config.
- Make the embedded registry the first Docker Hub mirror of the WITH
  DOCKER daemon, so it pulls those images by digest from BuildKit. Unknown
  content is a 404 and the daemon moves on to the other mirrors.
- Don't retag such images: the daemon pulls them by digest when used, and
  they no longer take part in tag collisions.

Mirrors only apply to Docker Hub in dockerd, so other registries keep the
retag path. Without the capability (older BuildKit), behaviour is as
before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

⚠️ Are we earthbuild yet?

Warning: "earthly" occurrences have increased by 2 (0.07%)

📈 Overall Progress

Branch Total Count
main 2741
This PR 2743
Difference +2 (0.07%)

📁 Changes by file type:

File Type Change
Go files (.go) ❌ +2
Documentation (.md) ➖ No change
Earthfiles ➖ No change

Keep up the great work migrating from Earthly to Earthbuild! 🚀

💡 Tips for finding more occurrences

Run locally to see detailed breakdown:

./.github/scripts/count-earthly.sh

Note that the goal is not to reach 0.
There is anticipated to be at least some occurrences of earthly in the source code due to backwards compatibility with config files and language constructs.

@kmannislands

Copy link
Copy Markdown
Author

Parking this PR pending #195 (containerd image store in dind). Details of the experiments are in the #195 comment above.

With the containerd store, a plain retag of an image's original content makes name@sha256:… resolve locally. That would make offline resolution work for any registry with no mirror, and replace the Docker Hub-only mirror approach in this PR. EarthBuild/buildkit#43 (serving the original content) is needed either way.

Plan:

  1. Merge fix: enable digests in compose files #513 independently.
  2. Size Enable containerd snapshotter for dind #195 (spike in progress).
  3. If Enable containerd snapshotter for dind #195 is close, rework this PR as "pull by original digest + retag" on the containerd store. If it's far off, reconsider landing this as an interim Docker Hub-only fix.

@kmannislands kmannislands self-assigned this Oct 7, 2026
@kmannislands
kmannislands force-pushed the giles-compose-digest-fix branch from 4a05617 to e0a792f Compare October 8, 2026 18:41
Base automatically changed from giles-compose-digest-fix to main October 14, 2026 22:36

This branch has not been deployed

No deployments
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.

2 participants