Skip to content
Latchkey

How to Push a Multi-Arch Image to ECR in GitHub Actions

A single multi-arch image manifest lets amd64 and arm64 hosts pull the right variant from one ECR tag.

Authenticate to AWS via OIDC, log in to ECR, then build and push both platforms in one manifest with buildx.

Steps

  • Configure AWS credentials with OIDC (aws-actions/configure-aws-credentials).
  • Log in to ECR with amazon-ecr-login.
  • Set up QEMU and buildx for cross-platform builds.
  • Build and push with platforms: linux/amd64,linux/arm64.

Workflow

.github/workflows/ecr.yml
permissions:
  id-token: write
  contents: read
jobs:
  push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gha-ecr
          aws-region: us-east-1
      - id: ecr
        uses: aws-actions/amazon-ecr-login@v2
      - uses: docker/setup-qemu-action@v3
      - uses: docker/setup-buildx-action@v3
      - uses: docker/build-push-action@v6
        with:
          push: true
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.ecr.outputs.registry }}/app:latest

Gotchas

  • The ECR repository must exist first; pushing does not create it.
  • Emulated arm64 via QEMU is slow; native arm64 runners are much faster.
  • Latchkey offers native arm64 runners so multi-arch ECR pushes are cheaper and avoid QEMU overhead.

Verify it actually works

A workflow that runs is not a workflow that works. Confirm the behaviour on a real event rather than on a manual dispatch, because trigger conditions, permissions, and context values all differ between the two.

Terminal
# 1. validate the file before pushing
docker run --rm -v "$(pwd):/repo" --workdir /repo rhysd/actionlint:latest -color

# 2. trigger the real event, not workflow_dispatch
git commit --allow-empty -m "ci: verify trigger" && git push

# 3. watch it and read the conclusion, not just the colour
gh run watch
gh run view --log-failed

What usually goes wrong first

  • The workflow file must exist on the default branch before scheduled or dispatch triggers appear at all.
  • GITHUB_TOKEN permissions default to read-only in many organisations. Declare a permissions: block listing every scope the job needs.
  • Fork pull requests get a read-only token and no access to secrets, regardless of workflow configuration.
  • actions/checkout gives you depth 1 on a detached HEAD, so anything needing history or a branch name needs fetch-depth: 0.

Frequently asked questions

How do I push a Multi-Arch Image to ECR in GitHub Actions?
Authenticate to AWS via OIDC, log in to ECR, then build and push both platforms in one manifest with buildx.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card