LAB-10: Multi-Platform Builds with Docker BuildKit
November 11, 2025 · View on GitHub
NEW 2024: Build Docker images that work on multiple architectures (AMD64, ARM64) with a single command!
Table of Contents
- 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
Overview
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.
Prerequisites
- Docker Desktop 4.0+ (includes buildx) OR Docker Engine with buildx plugin
- Basic understanding of Docker and Dockerfiles
- Internet connection (for pulling base images)
Why Multi-Platform Builds?
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
LAB-10.1: Setup Docker Buildx
Step 1: Check if buildx is available
docker buildx version
Expected output:
github.com/docker/buildx v0.12.0
Step 2: List existing builders
docker buildx ls
You should see the default builder.
Step 3: Create a new builder instance
# Create and use a new builder
docker buildx create --name multiplatform --use
# Bootstrap the builder (download necessary components)
docker buildx inspect --bootstrap
Step 4: Verify available platforms
docker buildx inspect multiplatform
Look 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!
LAB-10.2: Build Simple Multi-Platform Image
Step 1: Create a simple web application
mkdir multiplatform-test
cd multiplatform-test
Create 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>
Step 2: Create Dockerfile
Create Dockerfile:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
Step 3: Build for multiple platforms
# 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).
Step 4: Build for your current platform and test
# 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:8080
Step 5: Cleanup
docker stop testweb
docker rm testweb
LAB-10.3: Multi-Platform Go Application
Go is perfect for multi-platform builds with cross-compilation!
Step 1: Create Go application
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)
}
Step 2: Create multi-platform Dockerfile
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"]
Step 3: Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 \
-t multiplatform-go:latest \
-f Dockerfile .
Step 4: Test on your platform
# 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:8080
You should see output showing the OS and architecture!
Step 5: Cleanup
docker stop goapp
docker rm goapp
LAB-10.4: Multi-Platform Node.js Application
Step 1: Create Node.js application
Create 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}`);
});
Step 2: Create multi-platform Dockerfile
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"]
Step 3: Create .dockerignore
Create .dockerignore:
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
Step 4: Build multi-platform
docker buildx build --platform linux/amd64,linux/arm64 \
-t multiplatform-node:latest \
.
LAB-10.5: Build and Push to Registry
To use multi-platform images, you need to push them to a registry.
Step 1: Login to Docker Hub (or your registry)
docker login
Step 2: Build and push multi-platform image
# 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.
Step 3: Verify the manifest
# Inspect the image manifest
docker buildx imagetools inspect username/multiplatform-demo:latest
You should see multiple platform entries in the output!
Step 4: Pull and run on any platform
# This will pull the correct image for your platform
docker pull username/multiplatform-demo:latest
docker run -d -p 8080:80 username/multiplatform-demo:latest
Real-World Use Cases
Use Case 1: Development on Apple Silicon, Deploy on Linux AMD64
Problem: 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:latest
Use Case 2: Cost Optimization with AWS Graviton
Problem: 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 available
Use Case 3: IoT and Edge Computing
Problem: 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)
Best Practices
- 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
Troubleshooting
Issue: "exec format error"
Cause: Wrong architecture image
Solution: Build with correct --platform flag
Issue: Build very slow
Cause: Using QEMU emulation Solution: Use cross-compilation (see Go example)
Issue: Cannot load multi-platform
Cause: --load only works with single platform
Solution: Use --push to registry or build single platform
Summary
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!