Building multi-platform images with docker buildx and pushing to ECR with build cache

docker

A multi-platform image is an image that contains an index that is a list of manifests for multiple platforms.

$ cat Dockerfile
FROM --platform=$BUILDPLATFORM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .

ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 \
    GOOS=$TARGETOS \
    GOARCH=$TARGETARCH \
    go build -o /out/app .

FROM gcr.io/distroless/static-debian12
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]

$ docker buildx build --platform linux/amd64,linux/arm64 \
  --tag $ECR_REPOSITORY . \
  --push

In ECR, the Index appears alongside Images.

Clients can look at this Image Index and pull the image for the required platform. Attestations are metadata created by BuildKit that describes how the image was built and what it contains.

$ docker buildx imagetools inspect $ECR_REPOSITORY        
Name:      *****
MediaType: application/vnd.oci.image.index.v1+json
Digest:    sha256:73db34063986922f958fe9aa0dbb9a1962f9298e69b04b07c02e6697ee0fb126
           
Manifests: 
  Name:        *****@sha256:3486d283af679b5461f4c563861bed46634a3d580d0353ab8a460f4c061e35e8
  MediaType:   application/vnd.oci.image.manifest.v1+json
  Platform:    linux/amd64
               
  Name:        *****@sha256:8289b5c98cae3d6666f62a75661c7c43c5d9303d3fc0f69a35f414af7cde8a6a
  MediaType:   application/vnd.oci.image.manifest.v1+json
  Platform:    linux/arm64
               
  Name:        *****@sha256:f309b8f1aec49ea2e93021444d580e723726267a875cc681beac16c0eb4c4dcf
  MediaType:   application/vnd.oci.image.manifest.v1+json
  Platform:    unknown/unknown
  Annotations: 
    vnd.docker.reference.digest: sha256:3486d283af679b5461f4c563861bed46634a3d580d0353ab8a460f4c061e35e8
    vnd.docker.reference.type:   attestation-manifest
               
  Name:        *****@sha256:89dd415537cf2b40d4976d2a8363abbb45e720ad689b8315e2b76a7f3cca67b2
  MediaType:   application/vnd.oci.image.manifest.v1+json
  Platform:    unknown/unknown
  Annotations: 
    vnd.docker.reference.type:   attestation-manifest
    vnd.docker.reference.digest: sha256:8289b5c98cae3d6666f62a75661c7c43c5d9303d3fc0f69a35f414af7cde8a6a

In environments that creates a new environment every time like CI, you can speed up builds by using build cache saved externally with –cache-to type=registry.

$ docker buildx build --platform linux/amd64,linux/arm64 \
  --cache-from type=registry,ref=$ECR_REPOSITORY:buildcache \
  --cache-to   type=registry,ref=$ECR_REPOSITORY:buildcache \
  --tag $ECR_REPOSITORY . --push
  
$ docker buildx prune -a -f
  
$ docker buildx build --platform linux/amd64,linux/arm64 \
  --cache-from type=registry,ref=$ECR_REPOSITORY:buildcache \
  --cache-to   type=registry,ref=$ECR_REPOSITORY:buildcache \
  --tag $ECR_REPOSITORY . --push

 => CACHED [linux/arm64 build 2/6] WORKDIR /src                                                                                                                             0.0s
 => CACHED [linux/arm64 build 3/6] COPY go.mod go.sum ./                                                                                                                    0.0s
 => CACHED [linux/arm64 build 4/6] RUN go mod download                                                                                                                      0.0s
 => CACHED [linux/arm64 build 5/6] COPY . .                                                                                                                                 0.0s
 => CACHED [linux/arm64 build 6/6] RUN CGO_ENABLED=0     GOOS=linux     GOARCH=arm64     go build -o /out/app .                                                             0.0s
 => CACHED [linux/arm64 stage-1 2/2] COPY --from=build /out/app /app                                                                                                        0.0s
 => CACHED [linux/arm64->amd64 build 6/6] RUN CGO_ENABLED=0     GOOS=linux     GOARCH=amd64     go build -o /out/app .                                                      0.0s
 => CACHED [linux/amd64 stage-1 2/2] COPY --from=build /out/app /app   

ECR supports this and it appears as Other.