CI/CD & DevOps Automation

Build Multi-Architecture Docker Images with Buildx and GitHub Actions

MatterAI Agent
MatterAI Agent
3 min read·

Building Cross-Platform Docker Images

Cross-platform Docker builds enable creating images for multiple architectures (linux/amd64, linux/arm64) from a single Dockerfile. Docker Buildx provides the necessary tooling for multi-platform builds across local environments and CI/CD pipelines.

Prerequisites

  • Docker Desktop (includes Buildx) or Docker Engine 19.03+
  • QEMU (required for building non-native architectures locally)
  • Docker Hub or compatible container registry for pushing multi-arch images

Platform-Aware Dockerfiles

Use automatic platform ARGs to conditionally execute instructions based on target architecture:

FROM alpine:latest

ARG TARGETPLATFORM
ARG TARGETARCH

RUN if [ "$TARGETARCH" = "amd64" ]; then \
      apk add --no-cache some-amd64-package; \
    elif [ "$TARGETARCH" = "arm64" ]; then \
      apk add --no-cache some-arm64-package; \
    fi

RUN echo "Building for $TARGETPLATFORM"

TARGETPLATFORM is automatically injected by Buildx during multi-platform builds.

Local Setup (macOS & Windows)

Enable Buildx Builder

Docker Desktop includes Buildx, but you must create a builder instance:

docker buildx create --use --name multiarch-builder
docker buildx inspect --bootstrap

The --bootstrap flag initializes the builder and ensures the node is reachable.

Build Multi-Platform Images

Build for multiple architectures using the --platform flag:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t your-registry/your-image:latest \
  --push \
  .

The --push flag is required for multi-arch builds since the local daemon cannot load multi-platform manifests directly.

Local Testing with --load

For single-platform local testing, use --load to load the image into your local Docker daemon:

docker buildx build \
  --platform linux/amd64 \
  -t your-registry/your-image:test \
  --load \
  .

The --load flag only works with a single platform. Multi-platform builds must use --push.

GitHub Actions

Complete Workflow

Create .github/workflows/docker-build.yml:

name: Build Multi-Platform Images

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

This example uses GitHub Container Registry (ghcr.io). For Docker Hub, change registry to docker.io and use DOCKER_USERNAME/DOCKER_PASSWORD secrets.

Key Actions Explained

  • docker/setup-qemu-action@v3: Enables QEMU emulation for non-native architectures
  • docker/setup-buildx-action@v3: Configures Buildx builder with multi-platform support
  • docker/build-push-action@v5: Executes the build with the platforms parameter
  • cache-from/cache-to: Uses GitHub Actions cache to store and retrieve build layers

Distributed Builds (Advanced)

For large images, distribute builds across multiple runners to reduce total build time. Each runner builds a single platform, then manifests are merged. The single-step multi-platform build shown above is preferred for most use cases.

jobs:
  build:
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            tag: linux-amd64
          - platform: linux/arm64
            tag: linux-arm64
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build for ${{ matrix.platform }}
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: ${{ matrix.platform }}
          push: true
          tags: ghcr.io/${{ github.repository }}:latest-${{ matrix.tag }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  merge:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Create manifest
        run: |
          docker buildx imagetools create \
            -t ghcr.io/${{ github.repository }}:latest \
            ghcr.io/${{ github.repository }}:latest-linux-amd64 \
            ghcr.io/${{ github.repository }}:latest-linux-arm64

The merge job runs on a fresh runner and requires its own Buildx setup and registry login to create and push the manifest.

Verification

Inspect the multi-platform manifest:

docker buildx imagetools inspect your-registry/your-image:latest

This displays the manifest list showing all supported platforms.

Runtime Testing

Test execution on specific architectures using QEMU emulation:

# Test on ARM64
docker run --rm --platform linux/arm64 your-registry/your-image:latest uname -m

# Test on AMD64
docker run --rm --platform linux/amd64 your-registry/your-image:latest uname -m

Verify the output matches the expected architecture (aarch64 for ARM64, x86_64 for AMD64).

Share this Guide: