Multi-Arch Docker Builds at €14/Month - Just 2 VPSs

You don't need QEMU, GitHub Actions, or expensive CI services to build multi-architecture Docker images. With just two VPSs (one cheap ARM machine and one cheap x86 machine) you can build and push native linux/amd64 and linux/arm64 containers without emulation, without performance penalties, and without a monthly surprise bill. My setup costs just €14/month in total and reliably builds for both architectures using Docker's buildx feature. The key is combining docker buildx with SSH-based Docker contexts: each VPS becomes a node in a custom builder instance. The x86 VPS builds amd64 images locally, while the ARM VPS handles arm64 images natively. BuildKit takes care of the rest: parallel builds, multi-platform manifests, and direct pushes to the registry. It's clean, fast, and yours. No cloud CI, no opaque runners; just you, your servers, and full control over your builds. Set up the local amd64 docker node: docker buildx create --name multiarch --use --platform linux/amd64 Set up the remote arm64 docker node: docker context create arm-vps --docker "host=ssh://user@your.arm.vps.ip" Append the arm64 remote builder: docker buildx create --append --name multiarch --platform linux/arm64 arm-vps Inspect: docker buildx inspect rawpair-builder --bootstrap Name: multiarch Driver: docker-container Last Activity: 2025-04-26 16:31:34 +0000 UTC Nodes: Name: multiarch0 Endpoint: unix:///var/run/docker.sock Status: running BuildKit daemon flags: --allow-insecure-entitlement=network.host BuildKit version: v0.20.2 Platforms: linux/amd64*, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386 Labels: org.mobyproject.buildkit.worker.executor: oci org.mobyproject.buildkit.worker.hostname: baab70c53c2d org.mobyproject.buildkit.worker.network: host org.mobyproject.buildkit.worker.oci.process-mode: sandbox org.mobyproject.buildkit.worker.selinux.enabled: false org.mobyproject.buildkit.worker.snapshotter: overlayfs GC Policy rule#0: All: false Filters: type==source.local,type==exec.cachemount,type==source.git.checkout Keep Duration: 48h0m0s Max Used Space: 488.3MiB GC Policy rule#1: All: false Keep Duration: 1440h0m0s Reserved Space: 9.313GiB Max Used Space: 93.13GiB Min Free Space: 47.5GiB GC Policy rule#2: All: false Reserved Space: 9.313GiB Max Used Space: 93.13GiB Min Free Space: 47.5GiB GC Policy rule#3: All: true Reserved Space: 9.313GiB Max Used Space: 93.13GiB Min Free Space: 47.5GiB Name: multiarch1 Endpoint: arm-vps Status: running BuildKit daemon flags: --allow-insecure-entitlement=network.host BuildKit version: v0.20.2 Platforms: linux/arm64*, linux/arm/v7, linux/arm/v6 Labels: org.mobyproject.buildkit.worker.executor: oci org.mobyproject.buildkit.worker.hostname: aba4522bbd7d org.mobyproject.buildkit.worker.network: host org.mobyproject.buildkit.worker.oci.process-mode: sandbox org.mobyproject.buildkit.worker.selinux.enabled: false org.mobyproject.buildkit.worker.snapshotter: overlayfs GC Policy rule#0: All: false Filters: type==source.local,type==exec.cachemount,type==source.git.checkout Keep Duration: 48h0m0s Max Used Space: 488.3MiB GC Policy rule#1: All: false Keep Duration: 1440h0m0s Reserved Space: 9.313GiB Max Used Space: 93.13GiB Min Free Space: 47.5GiB GC Policy rule#2: All: false Reserved Space: 9.313GiB Max Used Space: 93.13GiB Min Free Space: 47.5GiB GC Policy rule#3: All: true Reserved Space: 9.313GiB Max Used Space: 93.13GiB Min Free Space: 47.5GiB As you can see, the local node builds amd64 images, the remote node builds arm64 images. I hope you find this useful.

Apr 26, 2025 - 18:17
 0
Multi-Arch Docker Builds at €14/Month - Just 2 VPSs

You don't need QEMU, GitHub Actions, or expensive CI services to build multi-architecture Docker images. With just two VPSs (one cheap ARM machine and one cheap x86 machine) you can build and push native linux/amd64 and linux/arm64 containers without emulation, without performance penalties, and without a monthly surprise bill. My setup costs just €14/month in total and reliably builds for both architectures using Docker's buildx feature.

The key is combining docker buildx with SSH-based Docker contexts: each VPS becomes a node in a custom builder instance. The x86 VPS builds amd64 images locally, while the ARM VPS handles arm64 images natively. BuildKit takes care of the rest: parallel builds, multi-platform manifests, and direct pushes to the registry. It's clean, fast, and yours. No cloud CI, no opaque runners; just you, your servers, and full control over your builds.

Set up the local amd64 docker node:

docker buildx create --name multiarch --use --platform linux/amd64

Set up the remote arm64 docker node:

docker context create arm-vps --docker "host=ssh://user@your.arm.vps.ip"

Append the arm64 remote builder:

docker buildx create --append --name multiarch --platform linux/arm64 arm-vps

Inspect:

docker buildx inspect rawpair-builder --bootstrap

Name:          multiarch
Driver:        docker-container
Last Activity: 2025-04-26 16:31:34 +0000 UTC

Nodes:
Name:                  multiarch0
Endpoint:              unix:///var/run/docker.sock
Status:                running
BuildKit daemon flags: --allow-insecure-entitlement=network.host
BuildKit version:      v0.20.2
Platforms:             linux/amd64*, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
Labels:
 org.mobyproject.buildkit.worker.executor:         oci
 org.mobyproject.buildkit.worker.hostname:         baab70c53c2d
 org.mobyproject.buildkit.worker.network:          host
 org.mobyproject.buildkit.worker.oci.process-mode: sandbox
 org.mobyproject.buildkit.worker.selinux.enabled:  false
 org.mobyproject.buildkit.worker.snapshotter:      overlayfs
GC Policy rule#0:
 All:            false
 Filters:        type==source.local,type==exec.cachemount,type==source.git.checkout
 Keep Duration:  48h0m0s
 Max Used Space: 488.3MiB
GC Policy rule#1:
 All:            false
 Keep Duration:  1440h0m0s
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#2:
 All:            false
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#3:
 All:            true
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB

Name:                  multiarch1
Endpoint:              arm-vps
Status:                running
BuildKit daemon flags: --allow-insecure-entitlement=network.host
BuildKit version:      v0.20.2
Platforms:             linux/arm64*, linux/arm/v7, linux/arm/v6
Labels:
 org.mobyproject.buildkit.worker.executor:         oci
 org.mobyproject.buildkit.worker.hostname:         aba4522bbd7d
 org.mobyproject.buildkit.worker.network:          host
 org.mobyproject.buildkit.worker.oci.process-mode: sandbox
 org.mobyproject.buildkit.worker.selinux.enabled:  false
 org.mobyproject.buildkit.worker.snapshotter:      overlayfs
GC Policy rule#0:
 All:            false
 Filters:        type==source.local,type==exec.cachemount,type==source.git.checkout
 Keep Duration:  48h0m0s
 Max Used Space: 488.3MiB
GC Policy rule#1:
 All:            false
 Keep Duration:  1440h0m0s
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#2:
 All:            false
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB
GC Policy rule#3:
 All:            true
 Reserved Space: 9.313GiB
 Max Used Space: 93.13GiB
 Min Free Space: 47.5GiB

As you can see, the local node builds amd64 images, the remote node builds arm64 images.

I hope you find this useful.