Unprivileged multi-arch builds — Victor Agnew

Victor Agnew

Staff AI Platform Engineer · Vancouver, BC

Boring deploys. work/ Safe AI. ai-lab/ Happy teams. virtual-office.md

PIPELINE
>20 min → <5 min
DEPLOYS
weekly → hundreds/week
FAILED PIPELINES
<1%
CREDENTIAL ROTATION
6 months → 24 h
REVIEW ENV
~$9/day
GCP SPEND
−40% YoY
MCP TOOLS
160+ → 23
VIRTUAL OFFICE
~1 h 40 min
virtual-office.md Virtual office three.js · Cloudflare Workers · LiveKit Built in a day; 26 days later, 1,600+ commits and 12 coworkers committing.
Close case-studies/multi-arch-builds.md Back

case-studies/multi-arch-builds.md

Unprivileged multi-arch builds

Container images for amd64 and arm64, built in parallel on matching GKE node pools and merged into one, with no privileged containers.

ArborXR · Staff Platform Engineer

Architectures
amd64 + arm64
Privileged containers
none
Runners for both architectures
one
  • Kaniko
  • GitLab CI
  • GitLab Runner
  • manifest-tool
  • GKE
  • Pulumi

The problem

We needed container images for both amd64 and arm64, and security compliance meant no privileged containers. That ruled out docker buildx and buildah, which both depend on them. Kaniko builds without privileges, but only for the architecture of the machine it runs on. Starting from a multi-arch base image doesn't help: Kaniko's output is still single-architecture.

What I built

So I worked with that limit instead of against it: build each architecture on matching hardware, then merge the results in the registry.

An arm64 node pool

I added an arm64 node pool to the GKE cluster with Pulumi: t2a-standard-8 machines, autoscaling from 1 to 20 nodes. GKE taints arm64 nodes automatically, so amd64 workloads stay off them.

One runner for both

One GitLab Runner serves both architectures. Its node_selector_overwrite_allowed option, undocumented at the time, lets each job override the node selector through a variable, so every build lands on nodes of its own architecture.

The helper image had to be multi-arch too. The arm64 helper images on Docker Hub were single-architecture and carried the wrong OS/ARCH label, and an alternative helper image broke the pipelines immediately. So I built a multi-arch helper image of my own and pointed the runner at it. The runner runs with privileged mode off.

Build, then merge

The amd64 and arm64 Kaniko builds run in parallel in a stageless pipeline. Once both finish, a final job runs manifest-tool, which reads their manifests remotely from the registry and combines them into one multi-arch image.

[ FIG. 1 ] One runner, two builds, one image
One runner, two builds, one image Left to right. A "GitLab CI pipeline" box → one "GitLab Runner" box (small tag "privileged = false", note "jobs override the node selector"). From the runner, two parallel arrows labelled "amd64" and "arm64" into a "GKE cluster" box holding two node pools stacked vertically: "amd64 nodes" (top, sub-label "existing pool") and "arm64 node pool" (bottom, sub-label "t2a-standard-8 · autoscales 1–20 nodes · tainted by GKE"). Each pool holds one "Kaniko build" job, and the two jobs run at the same time. Each job → a "Registry" box, arriving as two single-architecture images, "amd64" and "arm64". Registry → a "manifest-tool" job (sub-label "needs both builds · reads manifests remotely") → a final "Multi-arch image" box: one name and tag covering amd64 and arm64. GitLab CI pipeline GitLab Runner privileged = false jobs override the node selector amd64 arm64 GKE cluster amd64 nodes existing pool Kaniko build arm64 node pool t2a-standard-8 · autoscales 1–20 nodes · tainted by GKE Kaniko build Registry amd64 arm64 manifest-tool needs both builds · reads manifests remotely Multi-arch image one name and tag covering amd64 and arm64

Results

  • Both architectures build in parallel and land in the registry as one multi-arch image.
  • No build needs a privileged container: Kaniko runs unprivileged, and the runner's privileged mode is off.
  • One runner schedules build pods on both node architectures.

Read the full write-up (opens in a new tab)

next case study Virtual office