Every 2026 guide to preview environments converges on the same two cost levers: tear the environment down on a timer, and schedule it onto spot capacity for up to 90% off. Run the arithmetic against a box you own and the second lever turns out to be a tie.
A Hetzner EX44 — 20 threads, 64 GB, two NVMe drives — costs €57.30/month. Amortize the €39 setup fee over a year and that's $65.39/month, or $0.0896 per node-hour. The nearest rented shape, an m7i.4xlarge (16 vCPU, 64 GiB) in us-east-1, lists at $0.8064/hour — $588.67/month if you keep it running. Owning the box costs 11.1% of renting it. Put differently: the discount you'd need on a spot node just to match hardware you already bought is 88.9% — which is, to the decimal, the "up to 90%" number in the headline you were told to chase.
That's the whole finding. Spot's advertised ceiling is roughly the break-even point against owned hardware, and almost nobody gets the ceiling. Here is the table, the sensitivity, and what the knobs actually are once the meter is gone.
The same 64 GB, priced six ways
The comparison below holds memory constant — both boxes have exactly 64 GB — and runs the rented side across the full range of spot discounts a real team encounters, not just the two most convenient points. "Break-even hours" is the monthly node-hours below which renting is cheaper than the fixed $65.39; a month has 730 hours.
A fair warning on the CPU axis, since that's where this kind of comparison usually cheats: the EX44 shows Kubernetes 20 threads, but eight of its fourteen cores are efficiency cores, against sixteen Sapphire Rapids vCPUs on the m7i. Call it a wash at best. The margin below is large enough that it doesn't matter — handicap the EX44 by a full 2× on CPU and on-demand still costs 4.5× the owned box.
| Option | $/node-hour | $/month at full duty | vs. owned | Break-even hours/month |
|---|---|---|---|---|
m7i.4xlarge on-demand | $0.8064 | $588.67 | 9.0× | 81 (11%) |
| Spot at 40% off (effective, incl. retries) | $0.4838 | $353.20 | 5.4× | 135 (19%) |
| Spot at 55% off (common instance types) | $0.3629 | $264.90 | 4.1× | 180 (25%) |
| Spot at 60% off (effective, upper end) | $0.3226 | $235.47 | 3.6× | 203 (28%) |
| Spot at 75% off (common types, best case) | $0.2016 | $147.17 | 2.3× | 324 (44%) |
| Spot at the 90% headline | $0.0806 | $58.87 | 0.9× | 811 (111%) |
| Hetzner EX44, owned | $0.0896 | $65.39 | 1.0× | — |
Two things fall out of that table, and they point in opposite directions. Read them both.
At the literal headline rate, renting wins — barely. A spot node at exactly 90% off run continuously for a full month costs $58.87 against the EX44's $65.39. That's a 10% edge, and the break-even column says 811 hours, more than a month contains: there is no duty cycle at which the fixed cost catches up. If you genuinely get 90% off, rent. That's not a rhetorical concession; it's what the numbers say, and it's why the headline number is worth interrogating rather than dismissing.
At every rate anyone actually pays, owned hardware wins by 2–5×. The realistic band puts spot at $147–$353/month against $65. And the break-even column is the honest caveat in the other direction: below roughly 180–320 node-hours a month — a fleet that needs its node less than a quarter to a half of the time — renting is genuinely cheaper, because you stop paying for the idle. A three-person team with two open pull requests a week should rent. A team whose preview fleet is busy during every working day should not.
One line the table leaves out: egress. The EX44's traffic is unmetered; AWS bills $0.09/GB past the first 100 GB. For preview environments this is small — a reviewer clicking through a staging app moves megabytes — but a per-PR environment that pulls a 900 MB image on every rebuild is not nothing, and it only ever moves the number one way.
What "up to 90%" actually assumes
The headline is real, and it is also the maximum of a distribution whose middle is somewhere else entirely.
- AWS advertises up to 90% off on-demand, but the commonly requested families —
m6i,c7i,r7ginus-east-1— sit at 55–75% off, and analyses of effective savings after interruption, retries, and engineering overhead land at 40–60% for most teams in 2026. The 90% figure describes a rarely used instance family in an off-peak region. - Google's Spot VMs are explicitly dynamic, between 60% and 91% off, recomputed per region roughly monthly from rolling historical usage. You are quoted a discount, not promised one.
- Interruption is the price of the discount: historically under 5% average across instance types, with a 2-minute warning on AWS and 30 seconds on GCP, varying materially by type and availability zone.
Every one of those numbers is a discount on a meter. That's the assumption the advice never states, because on a rented substrate it's invisible: the cost of a preview environment is a rate multiplied by seconds, so the two ways to reduce it are to lower the rate (spot) or reduce the seconds (TTL, sleep schedules). Delete the meter — flat-rate hardware, paid whether or not a pull request is open — and one of those levers has nothing to pull on. The rate is already zero at the margin. Scheduling a preview pod onto a "spot" node on a fleet you own is a taint and a toleration that changes no line on any invoice.
What the advice costs on owned hardware isn't dollars, then. It's the engineering you'd spend implementing it: node pools you don't need, tolerations, disruption budgets tuned for preemption that never comes, and a dashboard tracking a savings number that is structurally zero.
On owned hardware the currency is headroom
The meter is gone but scarcity isn't. It just changed units. On a flat-rate box the constrained resource is allocatable CPU and memory — a fixed integer you cannot buy more of this month — and the thing that consumes it is not usage. It's requests.
Take the EX44. After kubelet and system reservations plus a per-node DaemonSet floor (CNI, ingress, node-exporter, a log shipper), call it 17 allocatable CPU and 56 GB. Now price a preview environment two ways:
| Per-PR environment | Requests | Envs per EX44 | Bound by | Cost per env-slot/month |
|---|---|---|---|---|
| Right-sized from observed usage | 0.5 CPU / 1 GB | 34 | CPU | $1.92 |
| Copy-pasted from production manifests | 2 CPU / 4 GB | 8 | CPU | $8.17 |
Same machine. Same invoice. Padding costs you 26 of 34 slots — a 4.25× reduction in how many pull requests your team can have open at once, and it shows up not as a bill but as a Pending pod and a developer waiting on a reviewer link.
This is not a hypothetical failure mode; it's the industry baseline. Cast AI's 2026 State of Kubernetes Optimization Report, drawn from tens of thousands of production clusters, measured CPU utilization at 8% (down from 10% the year before) against CPU over-provisioning at 69% and memory over-provisioning at 79% — memory utilization sits at 20%. Datadog's container research puts 83% of container spend on idle resources. On rented infrastructure that gap is a bill somebody eventually notices. On owned infrastructure it's invisible until the box is full, which is precisely why nobody fixes it.
Three knobs that actually move the number
Here's what replaces "put it on spot" when the meter is gone.
1. Bin-pack on purpose
The default scheduler scoring strategy, LeastAllocated, spreads pods across nodes to keep every node's utilization low. That's tuned for a world where a cluster autoscaler will add a node under pressure — a world you're not in with a fixed fleet, where spreading just means every node is too fragmented to hold the next environment. NodeResourcesFit supports the opposite:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- plugins:
score:
enabled:
- name: NodeResourcesFit
weight: 1
pluginConfig:
- name: NodeResourcesFit
args:
scoringStrategy:
type: MostAllocatedMostAllocated fills nodes before starting new ones. On a fleet where the node count is fixed and paid for, that's the correct default — and it's worth scoping to a dedicated scheduler profile that preview workloads request via schedulerName, so you're not bin-packing your production tier into a single failure domain.
2. Requests that don't lie
The 4.25× in the table above is the entire prize, and it's available without buying anything. Drive requests from observed p95 usage rather than from production manifests: a Vertical Pod Autoscaler in recommender-only mode against the preview namespace gives you the numbers in a week. Then make honesty structural with a LimitRange that sets a modest default request and a hard maximum in the preview namespace, so a copy-pasted 2-CPU request from a production chart is rejected at admission instead of quietly eating four slots.
The failure this prevents is worth naming: preview environments get their manifests from the same Helm chart as production, and production's requests are sized for production traffic. A preview environment serving one reviewer needs a rounding error of that.
3. Sleep, then delete
Scale-to-zero on owned hardware returns slots, not money — say that out loud, because a dashboard promising dollars saved will be wrong and the policy will get cancelled. But slots are exactly what you're short of.
A kube-green SleepInfo that keeps a preview namespace awake weekdays 08:00–20:00 and scales it to zero otherwise leaves it running 60 of 168 hours a week — a 36% duty cycle, a 2.8× multiplier on effective capacity. Combined with right-sized requests, one €57.30 box supports roughly 95 concurrent open pull requests instead of the 8 you get from padded, always-on environments. That's a 12× swing on identical hardware, and spot's contribution to it is exactly zero.
Deletion is the other half. An Argo CD ApplicationSet with the pull request generator and prune: true creates an Application per open PR and removes it — and every resource it owns — when the PR merges or closes. Add a TTL annotation as a backstop for the PR that stays open for six weeks. Reported ephemeral-environment costs of $0.12–$0.48 per environment-hour on rented infrastructure are the number this eliminates; on your own box it's the number of Pending pods it eliminates.
Build the preemption tolerance anyway
Here's the part of the spot playbook worth keeping even with no spot node in sight: your nodes still disappear. A Cluster API MachineDeployment rollout replaces every machine in the pool on a Kubernetes minor upgrade. Kernel and CVE patches drain nodes. Disks fail, and on hardware you own, nobody else is going to notice first.
So the interruption-tolerance work is not spot-specific engineering — it's fleet engineering that the spot literature happens to describe well. Concretely, for preview environments:
- A stable per-PR hostname at the Gateway or Ingress, e.g.
pr-1234.preview.example.com, resolved through a Service. A reviewer link that survives a reschedule is the difference between a node drain being invisible and it being a Slack thread. - Per-PR data seeded by a declarative Job, from a sanitized snapshot, running as part of the environment's own manifest set. If the seeding step lives in someone's terminal history, the environment does not survive its first eviction.
- A warm, shared build cache so a rebuild after a reschedule takes seconds. This is the single highest-leverage item, because it converts "the environment is gone" into "the environment is back."
- Deliberately no PodDisruptionBudget on preview workloads. A PDB on an ephemeral environment doesn't protect anything; it just blocks
kubectl drainand stalls your fleet upgrade behind a pull request nobody is looking at. Let them go.
Do that work and node replacement stops being an event. It also, incidentally, makes spot capacity a real option later — which brings us to the case for it.
When spot is genuinely the right answer
The honest counter-case has two parts, and neither is "run your preview environments on spot."
Burst above an owned baseline. Release weeks, an all-hands hackathon, a migration that opens forty PRs in three days — demand that exceeds your fixed capacity for a bounded window. Owning capacity sized for the peak means paying for the peak all year. This is the textbook hybrid: own the baseline, rent the spike, and the spike is exactly the workload whose break-even (well under 180 node-hours/month) sits on the rented side of the table above.
CI build runners, not preview environments. If you're going to put something on preemptible capacity, put the workload that is short, retryable, and has no human attached. A build job that gets preempted retries and nobody notices. A preview environment is long-lived, stateful-ish, and has a reviewer waiting on the link — the worst possible profile for a node that vanishes on 30 seconds' notice. The spot playbook is right about the technique and wrong about which workload to point it at.
The number to put on the dashboard
Preview-environment cost advice was written for a substrate where cost is a rate times seconds. On hardware you own, the rate is fixed, the seconds are free, and the only number that moves is how many environments fit.
Track slots per node and cost per env-slot — $1.92 versus $8.17 on the same €57.30 machine — and the optimizations sort themselves. Right-sizing requests is worth 4.25×. A sleep schedule is worth another 2.8×. Bin-packing recovers the fragmentation that would otherwise waste both. Spot is worth 0×, unless you're already above your baseline, in which case rent the peak and go back to the table.
And keep the 88.9% in your pocket. The next time a cost-optimization deck opens with "up to 90% off," you'll know it's quoting the number where renting finally ties a box you could have bought outright.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, on a Cluster API fleet that treats node replacement as routine. Star the repo on GitHub or deploy your first app today.
Sources
- Cast AI — 2026 State of Kubernetes Resource Optimization Report
- Google Cloud — Spot VMs: save up to 91% off on-demand Compute Engine
- Amazon EC2 Spot Instances pricing
- Hetzner — price adjustment, 15 June 2026
- Hetzner EX44 dedicated server
- Kubernetes — scheduler configuration (
NodeResourcesFitscoring strategies) - EC2 Spot vs On-Demand: the true cost difference in 2026
- Sedai — Spot Instances in Kubernetes: architecture and cost guide 2026
- CloudZero — What is Spot Instance Advisor?
- Signadot — Preview environments for Kubernetes (2026)
- Argo CD ApplicationSet pull request generator for PR preview environments
- kube-green — scaling down dev and test when not in use
- m7i.4xlarge pricing and specs — Vantage



