NEW 2024: Build Docker images that work on multiple architectures (AMD64, ARM64) with a single command!
- Overview
- Prerequisites
- Why Multi-Platform Builds?
- LAB-10.1: Setup Docker Buildx
- LAB-10.2: Build Simple Multi-Platform Image
- LAB-10.3: Multi-Platform Go Application
- LAB-10.4: Multi-Platform Node.js Application
- LAB-10.5: Build and Push to Registry
- Real-World Use Cases
Multi-platform builds allow you to create Docker images that work across different CPU architectures with a single build command. This is essential in 2024 as ARM-based systems (Apple Silicon, AWS Graviton, Raspberry Pi) become increasingly common.
- Docker Desktop 4.0+ (includes buildx) OR Docker Engine with buildx plugin
- Basic understanding of Docker and Dockerfiles
- Internet connection (for pulling base images)
Common Platforms in 2024:
linux/amd64- Intel/AMD processors (traditional servers, most PCs)linux/arm64- ARM processors (Apple M1/M2/M3, AWS Graviton, Raspberry Pi)linux/arm/v7- 32-bit ARM (older Raspberry Pi models)
Benefits:
- One image works everywhere
- No need for separate builds per architecture
- Better developer experience
- Cloud cost savings (ARM instances are cheaper)
- Support for Apple Silicon Macs
docker buildx versionExpected output:
github.com/docker/buildx v0.12.0
docker buildx lsYou should see the default builder.
# Create and use a new builder
docker buildx create --name multiplatform --use
# Bootstrap the builder (download necessary components)
docker buildx inspect --bootstrapdocker buildx inspect multiplatformLook for the "Platforms" line - you should see multiple platforms like:
Platforms: linux/amd64, linux/arm64, linux/arm/v7, ...
Result: You now have a multi-platform builder ready!
mkdir multiplatform-test
cd multiplatform-testCreate index.html:
<!DOCTYPE html>
<html>
<head>
<title>Multi-Platform Docker Demo</title>
</head>
<body>
<h1>Hello from Multi-Platform Docker!</h1>
<p>This image works on AMD64 and ARM64!</p>
</body>
</html>Create Dockerfile:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80# Build for both AMD64 and ARM64
docker buildx build --platform linux/amd64,linux/arm64 \
-t multiplatform-nginx:latest \
--load .Note: --load loads the image for your current platform. For multi-platform, you'll need to push to a registry (see LAB-10.5).
# Build for current platform only
docker buildx build --platform linux/amd64 \
-t multiplatform-nginx:latest \
--load .
# Run the container
docker run -d -p 8080:80 --name testweb multiplatform-nginx:latest
# Test it
curl http://localhost:8080docker stop testweb
docker rm testwebGo is perfect for multi-platform builds with cross-compilation!
Create main.go:
package main
import (
"fmt"
"net/http"
"runtime"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from Multi-Platform Docker!\n")
fmt.Fprintf(w, "OS: %s\n", runtime.GOOS)
fmt.Fprintf(w, "Architecture: %s\n", runtime.GOARCH)
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}Create Dockerfile:
# syntax=docker/dockerfile:1.4
# Build stage - use build platform
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
# Build arguments
ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY main.go .
# Cross-compile for target platform
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -ldflags="-s -w" -o app main.go
# Final stage - minimal image
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /app/app .
EXPOSE 8080
CMD ["./app"]docker buildx build --platform linux/amd64,linux/arm64 \
-t multiplatform-go:latest \
-f Dockerfile .# Build and load for current platform
docker buildx build --platform linux/amd64 \
-t multiplatform-go:latest \
--load .
# Run and test
docker run -d -p 8080:8080 --name goapp multiplatform-go:latest
# Check the output
curl http://localhost:8080You should see output showing the OS and architecture!
docker stop goapp
docker rm goappCreate package.json:
{
"name": "multiplatform-node",
"version": "1.0.0",
"main": "server.js",
"dependencies": {
"express": "^4.18.2"
}
}Create server.js:
const express = require('express');
const os = require('os');
const app = express();
app.get('/', (req, res) => {
res.json({
message: 'Hello from Multi-Platform Node.js!',
platform: process.platform,
arch: process.arch,
hostname: os.hostname()
});
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});Create Dockerfile:
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY server.js ./
# Final stage
FROM node:20-alpine
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy from builder
COPY --from=builder --chown=nodejs:nodejs /app .
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]Create .dockerignore:
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
docker buildx build --platform linux/amd64,linux/arm64 \
-t multiplatform-node:latest \
.To use multi-platform images, you need to push them to a registry.
docker login# Replace 'username' with your Docker Hub username
docker buildx build --platform linux/amd64,linux/arm64 \
-t username/multiplatform-demo:latest \
--push .Important: The --push flag automatically pushes after building. You cannot use --load with multiple platforms.
# Inspect the image manifest
docker buildx imagetools inspect username/multiplatform-demo:latestYou should see multiple platform entries in the output!
# This will pull the correct image for your platform
docker pull username/multiplatform-demo:latest
docker run -d -p 8080:80 username/multiplatform-demo:latestProblem: You develop on M1/M2 Mac (ARM64) but deploy to AWS EC2 (AMD64)
Solution:
# Build for both platforms
docker buildx build --platform linux/amd64,linux/arm64 \
-t myapp:latest --push .
# On M1 Mac: Uses ARM64 variant
docker run myapp:latest
# On AWS EC2: Uses AMD64 variant
docker run myapp:latestProblem: Want to use cheaper ARM-based AWS Graviton instances
Solution:
# Support both traditional and Graviton instances
docker buildx build --platform linux/amd64,linux/arm64 \
-t myapp:latest --push .
# Deploy to mixed fleet
# - Graviton (ARM64): 40% cheaper
# - Regular EC2 (AMD64): When ARM not availableProblem: Deploy to Raspberry Pi (ARM) and regular servers (AMD64)
Solution:
# Build for all ARM variants and AMD64
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
-t iot-app:latest --push .
# Works on:
# - Raspberry Pi 4 (arm64)
# - Raspberry Pi 3 (arm/v7)
# - Cloud servers (amd64)- Use Multi-Stage Builds: Smaller final images
- Leverage Build Arguments:
TARGETPLATFORM,TARGETOS,TARGETARCH - Test on Multiple Platforms: Use QEMU or real hardware
- Push to Registry: Can't load multi-platform locally
- Use Cross-Compilation: Faster than QEMU emulation
- Cache Efficiently: Use BuildKit cache mounts
Cause: Wrong architecture image
Solution: Build with correct --platform flag
Cause: Using QEMU emulation Solution: Use cross-compilation (see Go example)
Cause: --load only works with single platform
Solution: Use --push to registry or build single platform
You've learned:
- ✅ Setting up Docker Buildx
- ✅ Building multi-platform images
- ✅ Cross-compilation techniques
- ✅ Pushing to registries
- ✅ Real-world use cases
Multi-platform builds are essential in 2024 for modern cloud-native applications!