Every Heroku-to-Kubernetes migration story ends with the same punchline: the bill got smaller, a lot smaller. Published accounts cluster between 40 and 80 percent — one team took a $270/month Heroku setup to $45 on Kubernetes, another cut infrastructure spend 40 percent moving off Heroku Enterprise, a third reported 35 percent from autoscaling and spot alone. Thefolk version says "Kubernetes is cheaper." The true version is more useful: three specific line items pay for almost the whole discount, and they are not the three you would guess.
This post opens the bill. Using public list prices current in 2026, I price the same three workload shapes — a small Rails app, a mid-size multi-service setup, and a large production fleet — on Heroku, on cloud Kubernetes, and on owned Hetzner hardware. Every side sums to a total, every delta is shown in dollars and percent, and at the end one line item is crowned the biggest payer. If you are still on Heroku after its February 2026 move to sustaining engineering, this is the math your migration decision actually rests on.
The bill up front: three shapes, three totals
Take three representative stacks, priced from Heroku's public tiers (Standard-1X $25, Standard-2X $50, Performance-M $250; Postgres Standard-0 $50 and Standard-4 $200; Key-Value Store Premium-0 $15):
| Shape | Heroku composition | Heroku total |
|---|---|---|
| S — single Rails app | 2× Standard-2X web ($100) + 1× Standard-2X worker ($50) + Postgres Standard-0 ($50) + Redis Premium-0 ($15) | $215/mo |
| M — web + API + workers | 6× Standard-2X ($300) + Postgres Standard-4 ($200) + Redis Premium-0 ($15) | $515/mo |
| L — large production fleet | 6× Performance-M ($1,500) + 4× Standard-2X ($200) + Postgres Standard-4 ($200) + Redis Premium-0 ($15) | $1,915/mo |
Now the same shapes on Kubernetes with on-demand cloud nodes (m-class at roughly $35/vCPU-month, self-managed Postgres and Redis on spare cluster capacity, plus the $73/month managed control-plane fee):
| Shape | K8s on-demand total | Delta vs Heroku |
|---|---|---|
| S | $70 (1 node) + $73 (control plane) = $143 | −$72 (−33%) |
| M | $280 (2 nodes) + $73 = $353 | −$162 (−31%) |
| L | $840 (3 large nodes) + $73 = $913 | −$1,002 (−52%) |
And with committed-use pricing on the compute (savings plans covering 40–60 percent depending on term):
| Shape | K8s committed total | Delta vs Heroku |
|---|---|---|
| S | $143 (too small to commit efficiently) | −$72 (−33%) |
| M | $112 + $73 = $185 | −$330 (−64%) |
| L | $336 + $73 = $409 | −$1,506 (−79%) |
There is your 52-on-demand, 68-with-commitments pattern, reconstructed from list prices rather than asserted. Note what already peeks through: the small app barely benefits, the large fleet saves four figures a month, and the control-plane fee eats a third of the small app's savings before labor enters the picture. Everything below explains why.
Payer one: compute bin-packing, or why dynos are priced for idleness
A Heroku dyno is a reserved slice, billed 24/7 whether your puma workers are saturated or asleep. A Standard-2X dyno costs $50/month for roughly a vCPU and a gigabyte of RAM — about $50 per vCPU-month. Equivalent on-demand cloud compute runs about $35 per vCPU-month, so even one-to-one the Kubernetes node is ~30 percent cheaper per unit.
But the real win is not the unit price. It is density. Six Standard-2X dynos reserve six gigabytes and six CPU slices that cannot share headroom with each other. On Kubernetes those same containers bin-pack onto two shared nodes, and the cluster autoscaler reclaims whatever they do not use. One well-documented migration cut its bill from $270 to $45 almost entirely this way: nothing got faster, the containers just stopped each renting an apartment and started sharing a house. An older Heroku-to-GKE move reported the same halving of per-instance cost for the same reason.
Honest caveat: bin-packing only pays when there is something to pack. The S shape saves $20 on compute before the control-plane fee — one container cannot share a house with itself.
Payer two: the data-layer markup, the single biggest line item
Here is the number most teardowns underplay. Heroku Postgres Standard-0 — 64 GB of storage on modest compute — costs $50/month, and Standard-4 costs $200. A comparable self-managed Postgres running on spare capacity inside your own cluster costs the marginal price of disk you already rent: effectively zero on the bill, plus your time. Redis is the same story: Premium-0 starts at $15 for what is, on your own node, a few hundred megabytes of RAM.
Watch what this does to the M shape. Heroku M is $515, of which $215 (42 percent) is data add-ons. The Kubernetes on-demand total is $353, of which data is $0 marginal. Decompose the $162 on-demand saving:
| Line item | Heroku | K8s on-demand | Saved |
|---|---|---|---|
| Compute | $300 | $280 | $20 |
| Postgres + Redis | $215 | $0 marginal | $215 |
| Control plane | $0 | $73 | −$73 |
| Total | $515 | $353 | $162 (31%) |
The data-layer de-markup ($215) is larger than the entire net saving — it single-handedly covers the control-plane fee and then some, while compute bin-packing contributes a modest $20 at this size. At L scale the same pattern holds with bigger numbers: the $200 Postgres tier and the rigid per-dyno compute multiply together. If the title promised one line item that pays for the discount, this is the leading candidate — and it is also the line item teams underestimate because "managed Postgres" feels like a rounding error until you price Standard-4.
The February 2026 low-cost plans (Postgres Mini at $5, Key-Value Store Mini at $3) soften this for hobby projects, but production tiers did not move.
Payer three: the commitment discount, which is not a Kubernetes win at all
Now the asterisk on every "68 percent" headline. The jump from −52 percent to −64/−79 percent in my tables comes entirely from committed-use pricing: EC2 Instance Savings Plans reach 72 percent off on-demand, Compute Savings Plans reach 66 percent, and Google's committed-use discounts reach the high fifties. Those discounts reward a one-to-three-year promise to keep spending — they are available to plain EC2, ECS, or any architecture that holds still long enough to sign.
Kubernetes does not create this discount; it merely makes it collectible, because bin-packed, autoscaled nodes give you the stable baseline commitment that spiky dyno usage never could. Credit it honestly: roughly a third of the headline "with reserved instances" saving belongs to the signature on the commitment contract, not to the orchestrator. Any team comparing "Heroku vs Kubernetes with 3-year reserveds" against plain Heroku is comparing a discounted price to a list price. Compare discounted to discounted — or at least name the portion — before declaring victory.
The missing row: bandwidth
The spec for this analysis names bandwidth as a line item, so here it is, with dollars. Heroku's quiet advantage: dyno bandwidth is unmetered — there is no egress line on a Heroku bill at any scale. Cloud Kubernetes inherits its provider's meter: roughly $0.09/GB outbound, so a service pushing 10 TB/month pays on the order of $800–900/month in egress alone, a line that can dwarf every saving above for media-heavy or API-heavy workloads. Owned hardware flips it again: Hetzner bundles generous included transfer per server with a small fixed per-TB overage, so bandwidth is effectively flat. Bandwidth rarely decides the migration — but for one workload in ten it reverses the answer, which is exactly why it gets its row instead of a footnote.
Synthesis: the attribution table and the crown
For the M shape, every dollar of the committed-case $330 saving, attributed:
| Source of saving | $ | Share of total delta |
|---|---|---|
| Postgres/Redis de-markup | $215 | 65% |
| Compute: bin-packing + unit price | $188 (committed) | 57% |
| Control-plane fee (negative) | −$73 | −22% |
| Net | $330 (64%) | 100% |
Crown, as promised: the data-layer markup is the single biggest payer. No other line item clears $200 on this shape; Postgres and Redis de-markup alone exceed the net saving. Compute commitment discounts are the biggest percentage lever — they are what stretch 31 percent into 64 — but they are a financing decision any stable architecture can sign, not a Kubernetes invention. Bin-packing is real but supporting cast until fleet scale, where it takes the lead on absolute dollars.
What the headline hides: the ops tax, folded into the net
None of the above includes the human line item, so fold it in now. The control-plane fee is already in every total ($73/month). Migration labor is a one-time cost — real accounts put it at weeks, like the three-month Heroku-to-AWS migration one team documented for a ~40 percent cut — and steady-state Kubernetes operations run a few engineer-hours a month even on managed control planes. Price an engineer-hour fully loaded and the rule of thumb writes itself: migrate for cost when the monthly infra delta exceeds a few engineer-hours; below that the tax eats the discount.
Apply it to the three shapes: S saves $72/month before labor — it never clears the tax, and teams this size are better served by Heroku's low-cost tiers or a single-box PaaS. M saves $330/month committed — it clears the tax within a quarter. L saves $1,500/month — the payback period is measured in weeks. The honest headline is not "Kubernetes is 68 percent cheaper." It is "Kubernetes is 68 percent cheaper above a scale threshold, and the threshold is lower than vendors admit but higher than migrators tweet."
The same ten shapes on owned hardware
Reprice M and L as a self-hosted Cluster-API fleet on flat-rate dedicated boxes (current Hetzner dedicated pricing: 64 GB RAM, NVMe, Ryzen-class machines around €49–64/month each):
| Shape | Owned-hardware composition | Total | Delta vs Heroku |
|---|---|---|---|
| M | 2× €49 boxes (HA pair, whole stack fits with headroom) ≈ $106 | $106 | −$409 (−79%) |
| L | 3× €64 boxes ≈ $208 | $208 | −$1,707 (−89%) |
Two structural differences, both permanent. First, there is no reserved tier to negotiate because the hardware is already owned — the on-demand/committed split that complicates every cloud comparison collapses to one flat number. Second, bandwidth is included, so the egress row that threatens cloud migrations simply does not exist. The tradeoff is the one the discount cannot hide: you absorb hardware failure, capacity planning, and the control plane yourself. Qovery's teardown makes the complementary point from the cloud side — the math flips with scale, and owned hardware flips it hardest. For teams already operating a fleet, M's $106/month makes even the committed-cloud $185 look marked up.
Who should migrate, in one paragraph
If your Heroku bill is under ~$300/month on Standard dynos and production data tiers, do not migrate for cost — the ops tax wins, and Heroku's sustaining-mode pricing is not getting worse. Between ~$500 and $2,000, the data-layer markup alone funds the move within a quarter, and commitments fund it twice over; this is the band where the 52–68 percent pattern actually lives. Above that, price owned hardware before signing cloud commitments, because flat-rate boxes with included bandwidth beat both. And whatever you run after the migration, keep the attribution table from this post taped to the wall: when the next bill surprises you, it will be one of these three line items, and now you know which one pays.
Still on Heroku after sustaining mode and weighing the exit? Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with a Render-compatible API so the migration is a retarget, not a rewrite. Star the repo on GitHub or deploy your first app today.



