Here’s the entire step-by-step guide in a single Markdown file you can share or paste with one click:
# Multi-Arch GraalVM Native Image Pipeline for “Jibber”
**You will learn how to:**
1. Clone the Jibber demo source
2. Build a Spring Boot “uber-jar” and AOT-compile it into a native ELF using GraalVM
3. Containerize that ELF in a minimal Debian image
4. Cross-compile for ARM64 & AMD64 with Docker Buildx + QEMU in one command
5. Publish the multi-arch image to GitHub Container Registry (GHCR)
6. Verify & smoke-test on Linux/ARM and Windows/Intel
7. *(Optional)* Make the package public & automate via GitHub Actions
---
## 0. Prerequisites
- Docker ≥ 20.10 with Buildx
- QEMU binfmt handlers
- GitHub PAT in `CR_PAT` (scope: `packages:write`)
- *(macOS/Rancher Desktop)*
```bash
sudo ln -sf $HOME/.rd/docker.sock /var/run/docker.sock
# or
export DOCKER_HOST=unix://$HOME/.rd/docker.sock
-
(Optional)
jq
CLI
export GHUSER=<Your Github UserID>
export IMAGE=jibber
export CR_PAT=<your_GHCR_PAT>
1. Download the Demo
git clone --depth 1 https://github.com/graalvm/graalvm-demos.git
cd graalvm-demos/native-image/spring-boot-microservice-jibber
2. Create Dockerfile.multiarch
Save this in your project root as Dockerfile.multiarch
:
# syntax=docker/dockerfile:1.5
############################################################
# 1) BUILD + NATIVE (AOT) with Oracle-hosted Community Image
############################################################
FROM container-registry.oracle.com/graalvm/native-image:21 AS build
# Install Maven
RUN microdnf install -y maven && microdnf clean all
WORKDIR /workspace
COPY pom.xml ./
COPY src ./src
# 1) mvn clean package -Pnative → target/*.jar
# 2) native:compile → produces target/benchmark-jibber (ELF)
RUN mvn clean package -Pnative -DskipTests native:compile
##############################
# 2) RUNTIME IMAGE
##############################
FROM debian:bookworm-slim AS runtime
# Install zlib (libz.so.1) and CA certs
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
zlib1g \
ca-certificates; \
rm -rf /var/lib/apt/lists/*
# Copy the compiled ELF
COPY --from=build /workspace/target/benchmark-jibber /usr/local/bin/jibber
RUN chmod +x /usr/local/bin/jibber
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/jibber"]
GraalVM AOT: the Native Image plugin performs reachability analysis, tree-shaking, and ahead-of-time compilation, yielding a standalone ELF.
3. Bootstrap Buildx + QEMU
docker buildx create --name multiarch --driver docker-container --use || true
docker run --privileged --rm tonistiigi/binfmt --install all
4. Authenticate to GHCR
docker logout ghcr.io || true
echo $CR_PAT | docker login ghcr.io -u $GHUSER --password-stdin
5. Prune & Remove Old Images
docker image rm ghcr.io/${GHUSER}/${IMAGE}:latest || true
docker buildx prune --all --force
6. Build & Push Multi-Arch
option 1) Build & Push Multi-Arch (Linux/macOS)
docker buildx build \
--file Dockerfile.multiarch \
--platform linux/arm64,linux/amd64 \
--no-cache \
--pull \
--push \
-t ghcr.io/naiveprince0507/my-app:latest \
.
option 2) Build & Push Multi-Arch (PowerShell on Windows)
docker buildx build `
--file Dockerfile.multiarch `
--platform linux/arm64,linux/amd64 `
--no-cache `
--pull `
--push `
-t ghcr.io/naiveprince0507/my-app:latest `
.
7. Verify Manifest List
docker buildx imagetools inspect ghcr.io/${GHUSER}/${IMAGE}:latest --raw \
| jq '.manifests[]
| select(.platform.architecture!="unknown")
| {os:.platform.os, arch:.platform.architecture}'
Should show:
{ "os":"linux", "arch":"arm64" }
{ "os":"linux", "arch":"amd64" }
8. Smoke-Test Your Service
Linux/ARM (e.g. M1/M2 Mac)
docker run --rm -p 8080:8080 ghcr.io/${GHUSER}/${IMAGE}:latest
Windows/Intel (AMD64)
docker run --rm -p 8080:8080 ghcr.io/${GHUSER}/${IMAGE}:latest
Then:
curl http://localhost:8080/jibber # random verse
curl http://localhost:8080/actuator/health # {"status":"UP"}
9. (Optional) Make Public
- Go to https://github.com/users/${GHUSER}/packages
- Select jibber → Settings → Visibility → Public
10. (Optional) CI/CD GitHub Actions
Add .github/workflows/docker-multiarch.yml
:
name: Publish multi-arch jibber
on:
push:
branches: [ main ]
paths:
- 'native-image/spring-boot-microservice-jibber/**'
- 'Dockerfile.multiarch'
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
file: Dockerfile.multiarch
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/jibber:latest
ghcr.io/${{ github.repository_owner }}/jibber:v${{ github.run_number }}
On every push to main, this builds both ARM64 & AMD64 and publishes latest
and a version tag.
🎉 Share this Markdown with your team for a turnkey, multi-arch GraalVM native-image pipeline.