How to build a Multi-Architecture Docker Image in Github Actions using multiple runners without QEMU
Created by SREDevOps.org | @sredevopsorg | @ngeorger
By following these steps and reviewing the workflow file, you can customize and use this multi-architecture build process in your own projects. Happy building!
This repository demonstrates how to build multi-architecture Docker images (for example, linux/amd64 and linux/arm64) using native GitHub runners instead of QEMU emulation. By leveraging separate native runners for each architecture, you get faster builds and higher fidelity (no emulation quirks) while still producing a single multi-arch image that you can push to GitHub Container Registry (GHCR).
Note: This workflow requires access to both an AMD64 runner (using
ubuntu-latest
) and an ARM64 runner (for example,ubuntu-24.04-arm
). Make sure your GitHub organization or repository has access to these runners.
The workflow is divided into two main jobs:
-
Build Job:
- Uses a matrix strategy to build images for each platform separately.
- Sets up a Docker context and configures Buildx to build images natively.
- Logs into GHCR and builds & pushes the image by digest.
- Exports the digest as an artifact for later use.
-
Merge Job:
- Downloads the digests from the build job.
- Generates Docker image metadata (tags, labels, and annotations).
- Uses Buildx to create a multi-architecture manifest list from the individual images.
- Pushes the merged manifest (which tells Docker which image to pull based on the host architecture).
This two-step process creates a “fat” image that automatically delivers the correct binary for the user’s architecture.
- Native Builds: Uses GitHub’s native runners for each platform (no QEMU).
- Multi-Arch Manifest: Automatically merges per-architecture images into one multi-arch image.
- Build Caching: Implements caching to speed up subsequent builds.
- OCI Metadata: Adds labels and annotations for better image provenance.
- Concurrency Control: Uses GitHub Actions concurrency to cancel outdated builds.
- A GitHub repository with this workflow set up.
- Access to GitHub-hosted runners for both linux/amd64 and linux/arm64.
- A GitHub token (automatically provided as
GITHUB_TOKEN
) with permission to push to GHCR. - A valid Dockerfile in the repository root (or modify the workflow’s context as needed).
-
Clone the Repository:
git clone https://github.com/sredevopsorg/multi-arch-docker-github-workflow.git cd multi-arch-docker-github-workflow
-
Review and Customize:
-
Dockerfile:
The provided Dockerfile is a simple example. Customize it as needed for your application or replace it with your own. -
Workflow File:
The GitHub Actions workflow is defined in.github/workflows/multi-build.yaml
. You can adjust parameters (e.g., caching options, labels, annotations) if needed.
-
-
Configure Secrets (if applicable):
- By default, the workflow uses the built-in
GITHUB_TOKEN
for authentication to GHCR. If you need to change registry credentials or add additional secrets, set them in your repository’s Settings > Secrets and Variables.
- By default, the workflow uses the built-in
-
Prepare Environment:
The first step in the build job sets an environment variable (PLATFORM_PAIR
) by replacing any/
characters in the platform name with-
. This ensures artifact names are valid. -
Checkout Code:
The repository is checked out using actions/checkout. -
Generate Docker Metadata:
The docker/metadata-action creates metadata (tags, labels, annotations) based on repository information. -
Setup Docker Context and Buildx:
- A new Docker context (named
builders
) is created. - The docker/setup-buildx-action is configured to use that context and the specific platform from the matrix (either
linux/amd64
orlinux/arm64
).
- A new Docker context (named
-
Login to GHCR:
The workflow logs in to GitHub Container Registry using theGITHUB_TOKEN
. -
Build and Push:
Using docker/build-push-action, the image is built with the specified platform, labels, and caching options. The image is pushed by digest and the resulting digest is exported. -
Artifact Upload:
The digest file is uploaded as an artifact (named with the platform pair) for use in the next job.
-
Download Digests:
The merge job downloads all the digest artifacts from the build job. -
Re-generate Docker Metadata:
Metadata is generated again to ensure consistency with the built images. -
Setup Buildx and Login:
The job sets up Buildx (this time without a custom context) and logs in to GHCR. -
Create Multi-Arch Manifest:
The workflow runsdocker buildx imagetools create
to merge the images (using the digest files) into a single multi-architecture manifest list. Annotations such as description, creation timestamp, and source URL are added. -
Manifest Inspection:
Finally, the merged image is inspected to verify that it contains the proper platform-specific entries.
-
Automatic Trigger:
Pushing changes to theDockerfile
or the workflow file (.github/workflows/multi-build.yaml
) on themain
branch will automatically trigger the workflow. -
Manual Trigger:
You can also trigger the workflow manually using theworkflow_dispatch
event.
Once the workflow completes, your multi-architecture image is available in GHCR. For example, if your repository is ghcr.io/your-org/your-repo
, you can pull the image as follows:
docker pull ghcr.io/your-org/your-repo:latest
Docker will automatically select the correct image based on your host’s CPU architecture.
Contributions, issues, and feature requests are welcome! Feel free to check Issues or submit a pull request.
This project is licensed under the MIT License.