Every "move your compute to ARM and save money" writeup about Hetzner cites the same shape of claim: Ampere Altra cores cost less per vCPU than x86, so a Cluster API node pool that defaults to CAX capacity is free money. Pull Hetzner's current price sheet and check that claim against today's numbers, and it doesn't hold up the way it used to — x86's Gen3 line now undercuts ARM on raw price per core at every tier. What still holds is a narrower, more useful claim: Ampere Altra wins on throughput per euro for compile- and connection-heavy workloads, by a few percent, not the double-digit "ARM is obviously cheaper" pitch. And none of that savings reaches a tenant's app unless the platform's build layer can actually produce an image that runs on an arm64 node — which, for a buildpacks-based git-push platform, is not automatic.
What Hetzner's 2026 Price Sheet Actually Says
Hetzner raised Cloud prices in 2026, and the increase landed unevenly across lines. Here's the current Cost-Optimized CX (x86, Gen3) tier against the CAX (Ampere Altra, arm64) tier, at matching vCPU/RAM/disk specs:
| Plan | vCPU | RAM | Disk | Price/mo | Price per vCPU |
|---|---|---|---|---|---|
| CX23 | 2 | 4 GB | 40 GB | €3.99 | €2.00 |
| CX33 | 4 | 8 GB | 80 GB | €6.49 | €1.62 |
| CX43 | 8 | 16 GB | 160 GB | €11.99 | €1.50 |
| CX53 | 16 | 32 GB | 320 GB | €22.49 | €1.41 |
| CAX11 | 2 | 4 GB | 40 GB | €4.49 | €2.25 |
| CAX21 | 4 | 8 GB | 80 GB | €7.99 | €2.00 |
| CAX31 | 8 | 16 GB | 160 GB | €15.99 | €2.00 |
| CAX41 | 16 | 32 GB | 320 GB | €31.49 | €1.97 |
(Both lines add €0.50/month for an IPv4 address.)
At every matching tier, x86 is now the cheaper core. CX53 bottoms out at €1.41/vCPU; CAX41, the cheapest ARM tier, doesn't get below €1.97. That's the opposite of "ARM costs less per core" — it's a byproduct of Hetzner's 2026 price increase landing lighter on the older CX Gen3 line than on CAX.
Raw price per core was never the whole story, though. Independent benchmarks of CAX11 against a comparably-sized CX instance — Nginx serving static files, Node.js handling concurrent connections, PostgreSQL processing transactions — found Ampere Altra ahead on throughput: roughly 17% more Nginx requests/sec, 15% more Node.js concurrent connections, and 6% more Postgres TPS. Those numbers were measured against older Hetzner pricing, before the 2026 increase, so they can't be read directly against the table above. Recomputed as throughput-per-euro at today's prices (index 100 = CX23's throughput per euro, CAX11 at €4.49 vs. CX23 at €3.99):
| Workload | CX23 (idx/€) | CAX11 (idx/€) | CAX advantage |
|---|---|---|---|
| Nginx (static) | 25.1 | 26.1 | +4.0% |
| Node.js (connections) | 25.1 | 25.6 | +2.2% |
| PostgreSQL (TPS) | 25.1 | 23.6 | −5.8% |
The honest picture: CAX still wins on throughput per euro for web-serving and connection-bound workloads, by low single digits, not by the 17% headline. On a transaction-heavy database workload, current pricing flips it — CX23 is the better buy per euro. There's no single "ARM is cheaper" answer here; the answer depends on which workload you're pricing, and the margin is thin enough that a platform shouldn't be betting a default node pool decision on it without checking the current price sheet, not last year's benchmark writeup.
The Catch No Cost Comparison Mentions
None of the numbers above matter if the image never runs on the node. That's the part cost comparisons routinely skip: a meaningful slice of the Docker images teams actually deploy still ship amd64-only, and that gap hasn't closed as fast as ARM cloud capacity has gotten cheap.
Headless Chrome is the case every team hits eventually. Puppeteer and Playwright-based services — PDF generation, screenshot APIs, scraping jobs, anything that spins up a real browser — routinely fail on arm64 with "the chromium binary is not available for arm64," because the upstream Chromium builds those tools bundle don't ship an arm64 artifact by default. Getting a working arm64 image means switching to a community-maintained image or wiring up a separate Chromium build step, not something a generic buildpack auto-detects and handles. It's not an isolated case, either — GitHub issues on tools as widely used as Grafana's k6 load tester and Infracost have asked for arm64 image publishing in 2026 and gotten no shipped resolution, and some maintainers publish arm64-only or amd64-only tags on Docker Hub while a full multi-arch manifest lags behind on secondary registries.
For a raw-Dockerfile platform, this is the tenant's problem to solve — they control the FROM line and can chase down an arm64 base image themselves. For a buildpacks-driven git-push platform, it's layered: the tenant's dependencies (like a Puppeteer package) can silently assume amd64, and the platform's own builder image has to support the target architecture before any of that gets resolved. Both layers have to clear the bar before an app can safely land on a CAX node — and a cost comparison that only checks the second layer already missed the first.
What Cloud Native Buildpacks Can and Can't Do Across Architectures
The builder-image layer is more solved than the tenant-dependency layer, but it has a specific, easy-to-miss limitation: pack cannot cross-compile. Cloud Native Buildpacks' current multi-architecture support builds an arm64 app image on an arm64 host, and an amd64 app image on an amd64 host — there's no supported path to build an app image for one architecture on a different one. If your build fleet runs entirely on amd64 CI runners, you cannot natively produce an arm64 image without either provisioning arm64 build capacity or falling back to QEMU emulation (pack build --platform linux/arm64 on an amd64 host), which works but comes with a real performance penalty on compile-heavy builds — the same class of workload where CAX's throughput advantage was supposed to pay off in the first place.
The builder ecosystem has caught up faster than the cross-compile limitation suggests. heroku/builder:24 supports both amd64 and arm64 natively, and Paketo's builders — including the UBI9/10 builders Red Hat shipped multi-arch support for in June 2026 — cover the same ground. Once you have per-arch images built (natively, or via accepted-penalty emulation), pack manifest create, pack manifest add, and pack manifest push assemble them into a single multi-arch manifest list in the registry, the same mechanism docker buildx build --platform linux/amd64,linux/arm64 --push produces. A tenant's image tag resolves to the right architecture automatically from that point on — but only if the build pipeline took the extra manifest-assembly step. A default pack build that skips it produces a single-arch image, silently.
The Silent Failure Mode
Here's what happens when a single-arch amd64 image lands on a CAX node pool anyway. It's not a build failure — the build succeeds, the image pushes, the deploy looks green. It fails at container start, on the node, after everything upstream already reported success:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web-7f9b6d4c-xk2pl 0/1 CrashLoopBackOff 4 3m12s
$ kubectl logs web-7f9b6d4c-xk2pl
exec /cnb/process/web: exec format errorThat's a CNB-built image's launcher trying to exec an amd64 binary on an arm64 kernel, and getting a syscall-level rejection — not a missing dependency, not a bad config, an actual binary format mismatch. It's also the worst possible point to discover an architecture problem: after the commit is pushed, after the build passed, after the deploy claimed success, and now presenting as a crash loop that looks like an application bug to whoever's debugging it. Nothing upstream of the scheduler had a reason to know the tenant's image never got an arm64 variant.
What a Cluster API Node Pool Actually Needs Before Defaulting to CAX
Provisioning cheaper cores through Cluster API Provider Hetzner (CAPH) is the easy part — HCLOUD_X_MACHINE_TYPE set to a CAX type per node pool is a one-line config change. Making it safe to schedule arbitrary tenant workloads onto that pool is the actual engineering task, and it has four concrete pieces:
- A build step that can target arm64 at all — native arm64 build capacity, or QEMU-emulated
pack build --platform linux/arm64with the performance penalty accepted as a cost of entry, using a builder that already ships arm64 support (heroku/builder:24, current Paketo builders). - Multi-arch manifest assembly, not a single-arch push —
pack manifest create/add/push(ordocker buildx --push) so the registry holds one tag that resolves per-node-architecture, rather than one image that only happens to match whichever pool it lands on. - Node pools split by architecture, not blended — separate CAPH-managed pools per
HCLOUD_X_MACHINE_TYPEfamily, each labeled withkubernetes.io/archso amd64-only and arm64-only workloads can be scheduled deliberately instead of by chance. - A scheduling gate that inspects the manifest before it schedules — check whether a tenant's pushed image actually has a
linux/arm64entry in its manifest list, and fall back to the amd64 pool when it doesn't. This is the step that turns the exec-format-error crash loop above into a decision made before deploy, not a symptom discovered after.
That fourth point is the one a cost comparison never has to think about, and the one that decides whether "cheaper ARM cores" is real savings or a footgun with good unit economics. It's exactly the kind of build-layer decision any Cluster API–based, buildpacks-driven platform has to make explicit before pointing a node pool at CAX capacity — provisioning the cheaper node is trivial; guaranteeing every tenant's build can actually land there isn't.
The Gap Is Closing, Slowly
None of this is a permanent state. Paketo's multi-arch UBI builders shipped mid-2026, pack manifest subcommands exist now where they didn't a couple of years ago, and Hetzner's own price sheet will move again before this post's numbers are stale. But today, in July 2026, "Hetzner's ARM instances are cheaper per core" isn't simply true — "cheaper" is workload-dependent and single-digit where it holds — and a Cluster API node pool that defaults tenant builds onto CAX capacity without a manifest-aware scheduling gate is provisioning a discount most of its tenants' images can't actually reach.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, provisioned through Cluster API on infrastructure you control. Star the repo on GitHub or deploy your first app today.



