Skip to content

WITH docker fails to pull images with digest #512

Description

@gnuletik

What went wrong?

When a service in a docker-compose.yaml consumed via WITH DOCKER --compose declares its image: with a digest pin, Earthbuild's compose-fetch step fails with refusing to create a tag with a digest reference and aborts the build.

The retag step uses the literal compose image: field as the docker tag target. The docker engine rejects any tag target containing @sha256: — so any digest-pinned reference, whether name:tag@sha256:… or name@sha256:…, breaks.

Minimal reproducer

Earthfile:

VERSION 0.8

repro:
  FROM docker:29-dind
  COPY docker-compose.yaml ./
  WITH DOCKER --compose ./docker-compose.yaml
    RUN docker compose ps
  END

docker-compose.yaml:

services:
  app:
    image: alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc

Run:

earth -P +repro

Observed output

+repro | Pulling 127.0.0.1:.../sess-…:img-0 and retagging as alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc
+repro | refusing to create a tag with a digest reference
+repro | Downloading of images failed
+repro | error creating lease: Canceled: grpc: the client connection is closing: context canceled
+repro | ERROR Earthfile:6:3
+repro |       The command
+repro |           WITH DOCKER RUN ...
+repro |       did not complete successfully. Exit code 1

Replacing the compose image: with alpine:3.20 (no digest) makes the build succeed.
The same error reproduces with a digest-only ref (image: alpine@sha256:d9e853…) — it's the @sha256: suffix on the retag target that the engine rejects, not the combination of tag + digest.

What should have happened?

Compose image: fields with digest pins are valid OCI references and are accepted by both Docker Engine and Compose v2 for docker pull / docker compose pull. Earthbuild's fetch-and-retag step should either:

  1. Detect digest-bearing refs and skip the retag (loading the image by content ID and letting compose resolve it by digest), or
  2. Retag under the tag-only form (name:tag) and rely on compose's existing digest-verification semantics, or
  3. Document this as an explicit limitation and surface a clearer error message pointing users to the WITH DOCKER --pull image@sha256:… workaround.

What earthly version?

earth --version
earth version vHEAD-8f019dd 8f019ddcc8464509fbefe55f36b5f9a599593094 darwin/arm64; macOS 26.4.1 homebrew-earthbuild

Buildkit Logs

No response

Other Helpful Information

Workaround

Wrap the digest-pinned reference in its own Earthfile target and load it via --load:

VERSION 0.8

alpine-pinned:
  FROM alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc
  SAVE IMAGE alpine:3.20

repro:
  FROM docker:29-dind
  COPY docker-compose.yaml ./
  WITH DOCKER \
      --compose ./docker-compose.yaml \
      --load (+alpine-pinned)
    RUN docker compose ps
  END
services:
  app:
    image: alpine:3.20

This works because the digest is consumed by FROM (Earthbuild's own pull path, which handles digests correctly), SAVE IMAGE saves under a tag-only ref, and --load injects the image tarball into the dind daemon without invoking docker tag against a digest-bearing target.

It's also significantly more verbose than the natural image: …@sha256:… form, requires one extra target per pinned image, and makes the digest pin live in a different file from the service it pins — which is the heart of why this is worth fixing in Earthbuild rather than papering over.

Related

  • Upstream tracking issue: earthly/earthly#1852 — sha-qualified images in WITH DOCKER --pull are not available by that name. Covers the same class of bug from a different angle (--pull rather than compose) and remains open with no fix merged.
  • Underlying engine restriction: docker/cli#4545docker tag refusing digest-bearing targets is intentional behavior in the engine; the fix must live in Earthbuild's retag step.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions