ARM vs x86, NVMe vs SSD, Shared vs Dedicated: Which VPS Specs Matter
Cloud

ARM vs x86, NVMe vs SSD, Shared vs Dedicated: Which VPS Specs Matter

Three spec sheets, three arguments people have without naming the mechanism. ARM bites you at build time, not runtime. NVMe bites you at commit time, not page-load time. Shared vCPU bites you in top, in a column most people never open.

Published September 1, 202611 min readUpdated Sep 6, 2026

Written by · Full-Stack Agentic AI Software Engineer — AI Agents, Automation & Revenue Systems for GTM/RevOps teams

In brief

Do ARM processors, NVMe storage, and dedicated vCPU actually matter for a small self-hosted app, or are they marketing checkboxes?

All three matter, but each one shows up in a different, specific place rather than as a general speedup. ARM (Ampere, AWS Graviton) is a real price/performance win, but it changes your Docker build process — an image built on an x86 laptop for an ARM server fails at runtime with exec format error unless you build multi-arch. NVMe vs SATA SSD shows up in write-heavy database commit latency and container build I/O, not in serving a cached static page. Shared vs dedicated vCPU shows up as steal time under contention, visible in top's %st column, and matters only when your workload has real concurrent CPU demand.

  • AWS itself publishes Graviton as 10-20% cheaper on list price with 25-40% better price-performance on typical cloud-native workloads — a real, provider-stated number, not something to re-derive
  • The ARM tax isn't runtime speed, it's the build pipeline: a container image built for x86 fails on an ARM host with 'exec format error' unless you build with Docker buildx for both platforms
  • NVMe's advantage over SATA SSD is queue depth and PCIe bandwidth, which shows up in fsync-heavy database writes and parallel build I/O, not in reading an already-cached file
  • Shared vCPU steal time (%st in top) is the actual symptom of noisy-neighbor contention; dedicated vCPU plans exist specifically to remove that variable

Evidence notes

AWS Graviton price-performance claims

AWS states Graviton instances list 10-20% below comparable x86 instances, with 25-40% better price-performance reported for typical cloud-native workloads. These are the provider's own published figures.

Docker multi-arch and exec format error

Running a container built for one CPU architecture on a host of a different architecture fails with 'exec format error' unless QEMU emulation or a genuine multi-arch image (via buildx --platform) is used.

Hetzner CAX11 (Ampere ARM)

2 vCPU / 4 GB RAM ARM instance at €5.99/mo, checked September 2026 — the cheapest ARM entry point in this cluster's provider set.

Continue with purpose

Three arguments show up in every VPS comment section, and all three get argued the same wrong way: as if the answer is a single number, faster or slower, better or worse. It isn't. Each of these specs matters in one specific place and is close to irrelevant everywhere else, and knowing which place is the difference between a real upgrade and a wasted one.

Is ARM actually cheaper, or just different?

Cheaper, and AWS says so itself rather than leaving it to third-party benchmarks: Graviton instances list at 10-20% below comparable x86 instances, and AWS reports 25-40% better price-performance on typical cloud-native workloads. On the VPS side, Hetzner's CAX11 — 2 vCPU, 4 GB RAM, Ampere ARM — runs €5.99/mo, priced right alongside its cheapest x86 tiers, not as a premium option. The processor design behind that number is straightforward: ARM's architecture favors more, simpler cores per watt over x86's fewer, more complex ones, which is also why Graviton and Ampere both lean on efficiency claims rather than raw single-core speed. Where ARM vs x86, NVMe vs SSD, shared vs dedicated meets a revenue team, the practical guidance lives with XenGrowth.

None of that is the part that actually bites people. The part that bites people is what happens the first time they deploy a container built on their x86 laptop to an ARM box.

It's also worth being specific about what "25-40% better price-performance" is actually a claim about, since it's easy to over-read. It's a statement about typical cloud-native workloads in AWS's own testing, not a guarantee for any specific app, and it's a price-performance ratio rather than a claim that Graviton is faster in absolute terms core-for-core. A workload that's I/O-bound rather than CPU-bound — most small self-hosted apps, per the sizing pillar — won't feel anywhere near that number directly, because the bottleneck it's measuring against isn't the one your app is actually hitting. The honest way to read it: ARM is cheaper for the same nominal specs, and that alone is often reason enough, independent of whether the workload is the kind that stresses a CPU hard enough to show the full efficiency gap.

What breaks when you deploy to ARM without knowing it?

A container image is compiled for a specific CPU architecture, not "Docker" in the abstract. Build an image on an Intel or AMD machine — which is what almost every laptop and CI runner is — and it contains x86_64 binaries. Push that image to an ARM host and try to run it, and the kernel finds an ELF binary it can't execute. The error is blunt: `exec format error`. It's not a permissions issue, not a missing dependency — the CPU literally cannot run those instructions.

The fix is Docker buildx with the `--platform` flag, building for `linux/amd64` and `linux/arm64` in the same push, either via QEMU emulation (slower, but needs no special hardware) or a genuine multi-arch build across two builders. Dockerfiles that download architecture-specific binaries directly — a compiled tool, a native Node module — need that download path to branch on the `TARGETARCH` build argument buildx sets automatically, or the multi-arch image builds cleanly and then fails at runtime on one of the two architectures anyway. This is the one place "just switch to ARM to save money" has an actual cost: not runtime performance, a build pipeline you have to get right once and then forget about. There is a longer treatment of the operations side of this in The XenGrowth resource library.

  1. Create a builder that supports multiple platforms: `docker buildx create --use`

  2. Build and push in one step for both architectures: `docker buildx build --platform linux/amd64,linux/arm64 -t you/image:tag --push .`

  3. If the Dockerfile downloads a prebuilt binary by URL, branch that URL on the `TARGETARCH` build argument buildx sets automatically, rather than hardcoding one architecture

  4. Test the image on the actual target architecture before trusting it, not just on the architecture you built it on — a clean multi-arch build can still fail at runtime if a dependency inside it wasn't actually compiled for both

There's a second, quieter version of this problem beyond the Dockerfile itself: native Node modules with compiled bindings — image processing libraries, some database drivers — ship prebuilt binaries per architecture, and an outdated or pinned version can simply not have an arm64 build available yet. That failure looks different from exec format error; it's usually a build-time error about a missing native binding, and it means the fix isn't in your Dockerfile at all, it's upgrading or replacing the dependency. Worth checking before committing a production stack to ARM, not after.

Scenario

Works?

Fix

Build on x86 laptop, deploy to x86 VPS

Yes, no changes needed

Build on x86 laptop, deploy to ARM VPS (single-arch build)

No — exec format error at container start

Build multi-arch with `docker buildx build --platform linux/amd64,linux/arm64`

Build on Apple Silicon Mac, deploy to x86 VPS

No, same failure in reverse

Same buildx multi-arch fix

Dockerfile downloads a compiled binary by URL

Fails on whichever arch the URL doesn't match

Branch the download on `$TARGETARCH`

NVMe vs SATA SSD — where does the difference actually show up?

Both are solid-state, so it's tempting to treat the distinction as marketing noise. It isn't, but it's also not where most people look for it. NVMe drives connect over PCIe with a command queue built for deep parallelism; SATA SSD is stuck behind an interface spec designed for spinning disks, capped well below what the flash underneath could actually do. That gap matters exactly when you're issuing many small I/O operations concurrently, and barely matters when you're doing one large sequential read.

Serving a static page that's already sitting in the OS page cache never touches the disk at all, so NVMe buys nothing there. Where it shows up is a database committing writes under real concurrency — Postgres calls fsync on every commit by default, which is a synchronous wait on the disk actually confirming the write landed, and that wait is exactly the kind of small, frequent, latency-sensitive operation NVMe's deeper queue depth handles better. It also shows up during container builds and image pulls, where Docker is reading and writing many small layers at once rather than one big sequential stream. XenGrowth on governed AI marketing workflows goes further into AI agents and marketing automation.

Postgres's write-ahead log makes this concrete rather than abstract: every commit writes to the WAL before the actual data files, and that write has to be confirmed durable before the client gets an acknowledgment. A workload with a high commit rate — lots of small transactions, not a few large ones — spends a disproportionate amount of its total latency waiting on exactly that confirmation, which is the specific operation NVMe's queue depth was built to handle well. Contabo's own tier lineup happens to track this split cleanly: its entry Cloud VPS 4 ships SATA SSD, while its higher tiers move to NVMe, so the disk-type upgrade and the RAM upgrade arrive bundled together rather than as something you choose independently.

  • NVMe matters: database commit latency under write concurrency, container build/pull I/O, anything doing many small concurrent operations

  • NVMe barely matters: serving cached static content, sequential reads of large files, read-mostly workloads with a small working set

  • The safest read on a spec sheet: if your workload is write-heavy Postgres or CI-style builds, pay for NVMe. If it's mostly serving pages from cache, don't pay a premium for it.

Shared vs dedicated vCPU — what does contention actually look like?

A shared vCPU is a time-sliced portion of a physical core that the hypervisor divides across every tenant on that host. Most of the time this is invisible, because most tenants on most hosts aren't maxing out their allocation simultaneously. The mechanism that makes it visible when they are is steal time — CPU cycles your VM was scheduled for but didn't get, because the hypervisor gave that slice to a different tenant instead.

You can watch it directly. Run `top` on a shared-vCPU box and the CPU summary line has a `%st` column alongside user and system time — that's steal time, reported by the hypervisor to the guest kernel. On a lightly-loaded host it sits near zero. On a host where several tenants are all under load at the same moment, it climbs, and your process genuinely gets less CPU than your plan says it should have, even though nothing in your own app changed. Dedicated vCPU plans — Hetzner's CCX line, or the equivalent tier on most providers — pin physical cores to your instance alone, so `%st` stays at zero regardless of what other tenants on the same physical host are doing. For the AI search, GEO and discovery angle, see XenGrowth on building one SEO and GEO content system.

Signal

What it means

%st near 0% consistently

No meaningful contention — shared vCPU is behaving like a dedicated one right now

%st spikes during specific hours/days

Other tenants on the same host are contending at those times — a pattern, not a one-off

%st sustained high during your own load

Your workload is competing hard for cycles the host doesn't have free to give — a real case for upgrading to dedicated

High %us/%sy but low %st

Your own process is the bottleneck, not contention — more vCPU on the same shared plan may still help

Steal time is the one metric that tells you whether "upgrade to more vCPU" or "upgrade to dedicated vCPU" is the actual fix. Checking CPU percentage alone can't distinguish the two.

Which of those two upgrades applies also depends on whether your workload is bursty or sustained. A shared plan's contention hurts most when your own load spikes at the same moment as everyone else's — a batch job that happens to run during the host's busy hours, say — which is inherently intermittent and hard to predict. A sustained, always-on workload feels contention differently: it's competing for the same slice continuously rather than occasionally, so even a modest average steal time adds up to a real amount of lost throughput over a day. The first case is often solvable by just moving the job to a quieter hour; the second genuinely calls for dedicated cores, because there's no quiet hour to move it to. Either way, the fix is specific to the pattern you actually observe, not a reflex upgrade to whichever plan has a bigger number next to it.

So which spec should you actually pay for?

ARM, if your stack is a straightforward Node or Next.js app and you're willing to build multi-arch images once — the price/performance case is real and the build fix is a one-time setup cost, not ongoing work. NVMe, if your database is write-heavy or you're running CI on the box itself, otherwise don't pay extra for it. Dedicated vCPU, only once you've actually watched `%st` climb on your current shared plan — paying for isolation against a contention problem you haven't measured yet is buying insurance against a symptom you don't have. Match the spend to the mechanism, not to whichever spec sounds most premium on the page.

The common thread across all three is the same: none of them are universally better, and all three are frequently sold as if they were. ARM's win is architectural efficiency, gated behind a one-time build fix. NVMe's win is queue depth, gated behind actually having a write-heavy workload to feed it. Dedicated vCPU's win is isolation, gated behind actually having neighbors worth being isolated from. Buy each one for the mechanism it solves, not for the reassurance of the newer-sounding word on the spec sheet. If you're still deciding which tier gets you these specs at all, what each price actually buys lays out where NVMe and dedicated options actually sit across Contabo and Hetzner's current lineups.

Further reading from XenGrowth

Where this work meets go-to-market

XenGrowth, who work on the commercial side of this writes for the teams who have to run ARM vs x86, NVMe vs SSD, shared vs dedicated day to day.

Which spec should you actually pay for?

Four questions about the workload rather than the spec sheet. Most sizing mistakes come from paying for the axis that wasn't the constraint.

1 / 4
What does the app spend most of its time doing?

Apply this article

How to turn insights into execution

A practical sequence for teams turning concepts into production outcomes.

ARMAmpereNVMeshared vCPUDocker multi-archVPS specscloud

Audit your current state

Map the bottlenecks and constraints connected to the article’s core problem.

Choose one bounded change

Test the most useful recommendation on one workflow before widening the scope.

Measure what changed

Keep the parts that improve the work, document what failed, and make the next decision from evidence.

Next step

Need help applying this in your stack?

I can translate these patterns into a concrete implementation plan for your team.

Discuss implementationBack to blog

Replies usually within 24 hours.

Next Steps

Continue reading

How I Self-Host PostgreSQL for My SaaS (and When I Wouldn't)

Running Postgres in a container is easy. Running it in a way that survives a redeploy, a full disk, and an eventual major-version upgrade is the actual job. Here's the setup, tuned against Postgres's own defaults, and the honest list of where managed wins outright.

Navigate

10 Mistakes That Break a Self-Hosted SaaS

None of these ten show up as a single dramatic outage. They show up as a disk that quietly fills, a rollback that turns out to be impossible, a backup nobody ever restored. Here's the mechanism behind each one, and the fix.

Navigate

Cloudflare R2 vs S3 vs MinIO for SaaS File Storage

The storage price per gigabyte is close enough across all three that it barely matters. Egress is where the decision actually gets made — and one of these three had a rough 2026 that changes the self-hosting math entirely.

Navigate

The Backup Strategy Every Self-Hosted SaaS Needs (3-2-1, Applied)

3-2-1 is easy to nod along to and easy to get wrong in the specific way that only shows up on the day you need it. Here's what it actually means for one VPS running Postgres and Docker volumes, not the generic version you've already skimmed past twice.

Navigate

Managed Postgres vs Self-Hosted: Supabase, Neon and a VPS Compared

Supabase bills you for compute whether anyone's querying it or not. Neon bills you almost nothing until someone is, then charges for the second it takes to wake up. A VPS charges you the same either way and hands you every operational job both of the others do for you. None of these is the right answer by default.

Navigate

My Rule for Deciding What to Self-Host and What to Keep Paying For

Self-hosting everything is a bad idea, and I can point to the exact service where I decided that on purpose. Here's the actual rule I use, not a survey of options — and the one counterexample that explains why the rule exists.

Navigate

My Complete Self-Hosted Stack for SaaS in 2026

Contabo, Coolify, Docker, Cloudflare, Postgres, R2, Resend, Uptime Kuma, Turborepo and Docker Hub. Here's every piece of the stack I actually run, what each one replaced, and why I picked it over the alternatives.

Navigate

The Simplest Production Architecture for a Bootstrapped SaaS

One box, a handful of managed pieces around the edges, and a very short list of things you're not allowed to build yet. Here's the architecture, priced out to $20 a month, and the exact signal that tells you when to add each thing you skipped.

Navigate

How I Secure a Fresh VPS Before Deploying Anything

A brand-new VPS gets scanned within minutes of getting an IP address. Here's the exact order I run through before a single container touches the box — and which of these steps are real protection versus which ones are just theatre.

Navigate

vCPU or RAM? How to Size a VPS for Static Sites, Databases and Traffic Spikes

Four readers asked four versions of the same question, and each one has a different honest answer. Static files want RAM for page cache, Postgres wants RAM for shared_buffers, and "slow under traffic" is usually neither CPU nor RAM.

Navigate