Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Multi-Architecture Docker Images

Learn to create multi-architecture Docker images with practical hands-on steps. Master cross-platform builds, manifest tooling, and troubleshooting for ARM64 and AMD64 targets in this Docker tutorial.

Focus: create multi-architecture docker images

Sponsored

When your CI pipeline builds an image but it fails to run on an ARM-based server like AWS Graviton or a Raspberry Pi, you're facing the classic single-architecture trap. Modern cloud and edge environments are a mix of x86_64 (AMD64) and ARM64 hardware; shipping only one architecture means some users can't pull your image. Multi-architecture Docker images solve this by bundling variants for different CPU architectures under a single tag, so Docker automatically selects the right build when pulling.

The problem this lesson solves

Imagine you develop on a Mac with an Apple Silicon (ARM64) chip and push an image. A teammate on a Linux server (AMD64) pulls it — and gets a cryptic error:

WARNING: The requested image's platform (linux/arm64) does not match the detected host platform (linux/amd64) and no specific platform was requested

Or worse, the container crashes immediately. Without multi-architecture support, you must manually tag and push separate images (e.g., myapp:arm64, myapp:amd64), then expect users to pick the right one. That's error-prone and scales poorly.

Key pain points:

  • Mixed hardware environments – CI runners on AMD64 but production on ARM64, or vice versa.
  • Edge/IoT devices – Raspberry Pis, Jetson Nanos, and other ARM boards need ARM64 images.
  • Developer friction – Users expect docker run myapp to just work, regardless of their CPU.
  • Manual overhead – Maintaining separate tags for each architecture is a maintenance nightmare.

This lesson teaches you to create a single multi-architecture Docker image that works across platforms, using Docker's built-in buildx plugin and manifests.

Core concept / mental model

A multi-architecture Docker image is not a single layer blob — it's a manifest list (also called a fat manifest) that references separate image manifests for each platform.

Think of it like a DVD box set:

  • The cover slip lists all languages supported. That's the manifest list.
  • Inside, each disc contains a different language (e.g., English, Spanish, French). Each disc is a platform-specific image (AMD64, ARM64, ARM v7).
  • When you put the DVD in a player, the player reads the cover slip and automatically loads the right disc. Docker does the same: it inspects the host's architecture and pulls the correct variant.

Key definitions:

Term Meaning
Manifest list A JSON object pointing to one or more platform-specific manifests, typically stored in the registry under the same tag.
Platform-specific manifest Describes layers and config for a single architecture (e.g., linux/amd64, linux/arm64).
buildx Docker CLI plugin that enables multi-architecture builds using QEMU emulation or remote builders.
QEMU A user-space emulator that allows an AMD64 host to build ARM64 binaries (or vice versa).

Buildx handles the complexity. You define a single docker buildx build command with --platform flags, and it produces all the manifests and pushes them to the registry as one unit.

How it works step by step

  1. Enable BuildKit – Docker Desktop ships with BuildKit enabled; on Linux, ensure DOCKER_BUILDKIT=1 or use docker buildx.

  2. Create a builder instance – Buildx manages its own builder that can use multiple nodes (local or remote). Run docker buildx create --name mybuilder --use to create and set it as the active builder.

  3. Set up emulation (if needed) – If your host can't natively build for a target platform (e.g., building ARM64 on AMD64), install QEMU binfmt handlers: bash docker run --privileged --rm tonistiigi/binfmt --install all

  4. Build with --platform – The magic line: bash docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \ -t myregistry.io/myapp:latest --push . This command: - Builds the image for all three platforms in parallel. - Creates a manifest list under myregistry.io/myapp:latest. - Pushes everything directly to the registry (local images are not stored unless you also use --load).

  5. Verify – Use docker buildx imagetools to inspect the manifest list: bash docker buildx imagetools inspect myregistry.io/myapp:latest

Node diagram (mental — words only):

[Build host (AMD64)]
  │
  ├── buildx → QEMU → linux/arm64 build (emulated)
  ├── buildx → QEMU → linux/arm/v7 build (emulated)
  └── buildx → native → linux/amd64 build
  │
  └── Registry: manifest list (myapp:latest)
       ├── linux/amd64 manifest
       ├── linux/arm64 manifest
       └── linux/arm/v7 manifest

Hands-on walkthrough

Prerequisites

  • Docker 20.10+ with buildx plugin (built into Docker Desktop)
  • A registry where you can push images (Docker Hub, GitHub Container Registry, or a local registry)
  • Sample project: a simple Node.js or Go app

Step 1: Create a simple app

Create a Dockerfile that is architecture-agnostic:

# Dockerfile
FROM alpine:latest
RUN apk add --no-cache curl
COPY hello.sh /hello.sh
RUN chmod +x /hello.sh
CMD ["/hello.sh"]

hello.sh:

#!/bin/sh
echo "Hello from $(uname -m)!"

Step 2: Set up buildx

docker buildx create --name multibuilder --use
docker buildx inspect --bootstrap

Expected output snippet:

Name:   multibuilder
Driver: docker-container
Nodes:
Name:      multibuilder0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/amd64, linux/amd64/v2, linux/arm64, linux/arm/v7, linux/arm/v6

Pro Tip: Run docker run --privileged --rm tonistiigi/binfmt --install all if you don't see ARM platforms listed.

Step 3: Build and push multi-architecture image

docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t <your-registry>/hello-multi:latest --push .

Expected behavior:

  • The command runs multiple builds in parallel.
  • On first build, it may be slower because QEMU emulation is used for non-native platforms.
  • No local image is created unless you add --load (but --load only works for one platform at a time).

Step 4: Verify the manifest list

docker buildx imagetools inspect <your-registry>/hello-multi:latest

Expected output (truncated):

Name:      docker.io/<your-registry>/hello-multi:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Manifests:
  linux/amd64
  linux/arm64
  linux/arm/v7

Step 5: Test on different architectures

Pull and run on an ARM64 machine (e.g., Raspberry Pi):

docker run --rm <your-registry>/hello-multi:latest
# Output: Hello from aarch64!

On amd64:

docker run --rm <your-registry>/hello-multi:latest
# Output: Hello from x86_64!

Pro Tip: To test locally without leaving your machine, you can use --platform flag with docker run and QEMU emulation, but that requires binfmt setup and runs your container under emulation (slower).

Compare options / when to choose what

Approach Pros Cons Best for
Docker buildx with --platform One command, automatic manifest list, parallel builds Requires buildx + QEMU emulation (slower); no local image for each platform without --load Most users; CI/CD pipelines
Manual manifest create + amend Full control, can reuse already-pushed platform images More steps, error-prone, easy to mis-match digests Advanced users, or when you have existing platform-specific images
CI matrix builds (e.g., GitHub Actions) Separate jobs per platform, no emulation bottleneck More complex config; manual merging of manifests Large teams; when native builders are available (e.g., macOS ARM runner + Linux AMD64 runner)

When to avoid multi-architecture:

  • You control all deployments and know they're all same architecture.
  • You're distributing to only one hardware type (e.g., all x86_64 servers).
  • The Dockerfile contains platform-specific instructions (e.g., FROM arm64v8/ubuntu) that cannot be trivially made portable.

When it's essential:

  • Publishing public images (Docker Hub, GitHub Container Registry).
  • Supporting both Apple Silicon (M1/M2/M3) developers and Linux servers.
  • IoT/edge computing where devices are ARM-based.

Troubleshooting & edge cases

Build fails or is very slow on ARM builds

  • Problem: QEMU emulation is slow — expect 3–10x slower than native.
  • Fix: Use a remote builder (e.g., an ARM EC2 instance) as a build node in buildx. Alternatively, use CI jobs with native ARM runners.

exec user process caused: exec format error

  • Problem: The container has binaries for the wrong architecture.
  • Fix: Verify you pushed a multi-arch image. Run docker buildx imagetools inspect. Ensure you're not accidentally pulling a single-arch image.

no matching manifest for linux/arm64 in the manifest list entries

  • Problem: The manifest list doesn't include an entry for your host's architecture.
  • Fix: Re-build with all required platforms. Check that --platform list covers your target.

Local --load only works for one platform

  • Problem: You want to test the multi-arch image locally but --load only stores one platform.
  • Workaround: Use docker buildx build --load --platform linux/amd64 for local testing, then rebuild with --push without --load for the full multi-arch push.

Using FROM with a non-multi-arch base image

  • Problem: Base image (e.g., ubuntu:18.04) may not support ARM64.
  • Fix: Use official multi-arch base images like alpine, ubuntu:22.04, or node:20-alpine. Check the registry manifest.

What you learned & what's next

You now understand how to create multi-architecture Docker images using docker buildx. You can:

  • Explain the concept of a manifest list vs platform-specific manifest.
  • Set up a buildx builder with QEMU emulation.
  • Write a docker buildx build command that builds for multiple platforms.
  • Verify the manifest list with docker buildx imagetools inspect.
  • Troubleshoot common issues like missing platforms or exec format errors.

This skill enables you to ship Docker images that work across developer laptops, CI runners, and production servers — regardless of CPU architecture.

What's next? Move on to optimizing image sizes with multi-stage builds, or explore running containers with resource constraints using --memory and --cpus. You're now equipped to build portable, platform-agnostic Docker images.

Practice recap

Mini exercise: Create a simple Go HTTP server Dockerfile that prints its listening architecture. Use buildx to build and push multi-architecture images for linux/amd64 and linux/arm64. Then pull and run both locally using docker run --platform to confirm the output differs. This cements the multi-arch workflow in a practical scenario.

Common mistakes

  • Forgetting to install QEMU binfmt handlers — without them, builds for non-native architectures fail silently or hang. Always run docker run --privileged --rm tonistiigi/binfmt --install all first.
  • Using --load and expecting a multi-arch image locally — --load only works for one platform; you must use --push to the registry for multi-architecture to work.
  • Assuming all base images are multi-architecture — e.g., ubuntu:18.04 may lack ARM64 support. Always check the registry with docker buildx imagetools inspect on the base image.
  • Overlooking --platform in the Dockerfile RUN commands — if you use RUN apt-get install and the package is architecture-specific, the build may succeed for one arch and fail for another.

Variations

  1. Using docker manifest create and docker manifest annotate manually instead of buildx — gives full control but requires separate docker push for each platform image first.
  2. Leveraging a CI matrix (e.g., GitHub Actions) with separate jobs per architecture, then merging manifests in a final step — avoids QEMU emulation slowdown.
  3. Using BuildKit directly via buildctl for advanced caching and remote builders — more flexible but less integrated than buildx.

Real-world use cases

  • Shipping a public Docker image that runs on both AMD64 servers and ARM64 AWS Graviton instances without separate tags.
  • Building an IoT application handler that must deploy to Raspberry Pi (ARMv7) and an x86_64 edge gateway from the same CI pipeline.
  • Developing a Microservices platform where each developer runs Docker Desktop on Apple Silicon while production runs on Linux AMD64 VMs.

Key takeaways

  • Multi-architecture Docker images use a manifest list to bundle platform-specific images under one tag.
  • Docker buildx with --platform is the recommended way to build cross-platform images in one command.
  • QEMU emulation enables building for non-native architectures on your host, but is slower than native builders.
  • Use docker buildx imagetools inspect to verify the manifest list after push.
  • Push to a registry; --load stores only one platform locally — multi-architecture only works when pushed.
  • Always choose multi-architecture base images like Alpine or official Ubuntu 22.04+ to avoid architecture mismatch.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.