Nobody migrates off Heroku because a single dyno is expensive. Twenty-five dollars for a managed container that deploys on git push, rolls back in one command, and never pages you at 3 AM is a bargain. Teams migrate because the bill is never one dyno. It is two web dynos plus a worker plus Postgres plus Redis, each priced like it is the only thing on the invoice — and then it repeats every month, forever, while the app itself would fit comfortably on a box the size of a lunch order.
So let us do the thing migration writeups usually skip: price one concrete, boring, typical stack on both sides, line by line, at September 2026 rate cards — and count the egress line honestly instead of assuming it. Here is the answer up front; every receipt follows below.
| Heroku (before) | Hetzner CX (after) | |
|---|---|---|
| Reference stack: Node.js API + worker + Postgres + Redis | $87/mo | ~$7/mo |
| Multiplier | — | ~12x cheaper |
| Annualized | $1,044/yr |
Four disclosures ride along with that table, because a multiplier without them is marketing. First, this is a list-price recompute from public pricing pages (Heroku's pricing page and Dev Center plan specs; Hetzner's post-June-15-2026 EU catalog), not a scanned invoice — but every rate below is cited so you can redo it. Prices are normalized at EUR→USD ~1.08 (September 2026), excluding VAT. Second, the after side is a single node with no HA or failover, running Postgres and Redis co-located with the app, versus multi-AZ Standard dynos and managed data services — the "where Heroku still wins" section prices what closing that gap costs.
Third, backups, snapshots, and ops/on-call time are excluded from both sides, which flatters Hetzner more than Heroku (Heroku's ops cost is bundled into its prices; Hetzner's is your evenings). Fourth, the savings are compute plus database; the egress delta between these two vendors is $0 versus $0 all the way to 20 TB, and the egress section shows exactly where egress actually bites.
The Heroku bill, line by line: $87
The reference stack is the smallest thing a real side-project SaaS actually runs in production: a Node.js API with two web dynos for rolling deploys, one worker dyno for background jobs, a small Postgres, and a small Redis for sessions and rate limits. No review apps, no Private Spaces, no Shield — plain Heroku, priced off the current rate card:
| Line item | Plan | Price |
|---|---|---|
| 2x web dynos | Standard-1X at $25 (512 MB RAM each) | $50/mo |
| 1x worker dyno | Standard-1X at $25 | $25/mo |
| Postgres | Essential-1 (10 GB, non-HA tier) | $9/mo |
| Redis | Key-Value Store Mini (25 MB) | $3/mo |
| Egress | Bundled with dynos — no per-GB meter | $0 |
| Total | $87/mo |
Two rows deserve commentary. Standard-1X at $25 with 512 MB is Heroku's entry-level production dyno (Eco at $5 sleeps and throttles; Basic at $7 is a single half-GB hobby box), and two of them is the minimum for zero-downtime deploys via preboot. Essential-1 at $9 is the honest small-prod database choice — but note what it is: the Essential tier tolerates hours of downtime a month with no failover, which is exactly why the growing-prod rung in the sensitivity ladder jumps to Standard-0 at $50 (64 GB, 4 GB RAM) when customer data is on the line.
And then there is the egress row, which is the most important $0 in this post. Heroku does not meter bandwidth. There is no per-GB egress charge on any dyno tier — third-party calculators list it flatly as "no charge for egress," and comparison tables put "included in dyno cost" where other vendors put a meter. This matters because the naive version of this article claims the multiplier "shows up in the egress line." It does not. Heroku's tax is collected entirely in compute and add-ons, and anyone telling you otherwise has not read the invoice.
The egress math still belongs in this post — but as the table below shows, it belongs as the story of where metered vendors punish you, not Heroku.
The Hetzner bill, line by line: ~$7
The identical containers — same Node.js API, same worker, Postgres and Redis co-located — run on a single Hetzner Cloud CX23 (2 shared vCPU, 4 GB RAM, 40 GB SSD) under a Cluster-API-managed single-node Kubernetes setup:
| Line item | Detail | Price |
|---|---|---|
| 1x CX23 (EU) | 2 vCPU / 4 GB / 40 GB, post-June-2026 catalog | |
| Transfer | 20 TB included; ~€1/TB overage | $0 at this stack's traffic |
| Backups/snapshots | Excluded (see disclosures) | $0 (not $0 cost — $0 counted) |
| Ops/on-call | Excluded (see disclosures) | $0 (not $0 cost — $0 counted) |
| Total | ~$7/mo |
The consolidation assumption, stated plainly so you can reject it: a lightly-loaded Node API plus a worker plus a few-GB Postgres fit in 2 vCPU and 4 GB with headroom, and this is the standard single-box shape every migration writeup converges on. The contention limit is equally plain: one noisy neighbor — a runaway worker, a Postgres vacuum storm, a traffic spike — and all three tiers degrade together, with no second node to fail over to.
If that sentence made you flinch, price the separated-DB alternative instead: a second CX23 holding Postgres and Redis costs €12/mo total ($13), both nodes still flat, and the multiplier settles at ~7x instead of ~12x. Still a rout; just an honest one.
One more pricing note, because Hetzner's 2026 catalog will confuse anyone working from an old bookmark: the box this post prices is the CX23, not the CX22. Hetzner raised cloud prices twice in 2026 — a broader adjustment of up to ~37% on April 1, then a second round effective June 15 that pushed dedicated-vCPU CPX/CCX lines up as much as ~113–176% while the cost-optimized CX/CAX lines rose a more modest ~30–38%, citing the DRAM and NVMe price shock. The same reshuffle renamed a generation (old CX22 → CX23), and the old name is now deprecated in the provisioning API. Anyone whose mental model still says "€4.49 Hetzner box" is quoting a pre-2026 catalog.
The honest framing cuts both ways: Hetzner narrowed its own discount this year and the multiplier below still starts at 7x.
Egress math: the 0 line, and where egress actually bites
Here is the table the title promised, at three monthly egress levels for the same stack:
| Monthly egress | Heroku | Hetzner CX23 (EU) | Metered-cloud context (illustrative only) |
|---|---|---|---|
| 0.5 TB | $0 (bundled) | $0 (inside 20 TB) | ~$45 at $0.09/GB EC2-style rates |
| 5 TB | $0 (bundled) | $0 (inside 20 TB) | ~$450 |
| 25 TB | $0 (bundled) | ~€5 (5 TB over 20 TB at ~€1/TB) | ~$2,250 |
Read it carefully. Between the two vendors this post compares, egress changes nothing: Heroku bundles it, Hetzner includes 20 TB per server — roughly two orders of magnitude above what this stack serves — with overage at about a tenth of a cent per GB. The third column is illustrative context only, not part of the before/after: it shows what the same traffic costs on a hyperscaler meter, which is the actual place egress "shows up" — and the reason egress belongs in a migration post at all.
Teams that outgrow bundled bandwidth do not usually land on EC2 egress pricing; they land on it from somewhere metered (a functions bill, a CDN-less object store, Fly.io's $0.12/GB Africa/India tier), and the Hetzner column is what "never think about it again" costs. The migration lesson is not "Heroku's egress is expensive" — it is that Heroku already solved egress by bundling it, and Hetzner matches that property at one-twelfth the compute price.
Sensitivity: hobby to growing prod
One workload is one data point, and multipliers computed at a single convenient scale are how cost posts lie. Here is the ladder, with the Hetzner size stated per rung:
| Stage | Heroku config | Heroku bill | Hetzner config | Hetzner bill | Multiplier |
|---|---|---|---|---|---|
| Hobby | 1x Basic ($7) + Essential-0 ($5) | $12/mo | 1x CX23 | ~$7/mo | ~1.7x |
| Small prod (mainline) | 2x Std-1X + worker + Essential-1 + KV Mini | $87/mo | 1x CX23 | ~$7/mo | ~12x |
| Growing prod | 2x Std-2X ($100) + worker ($50) + Standard-0 ($50) + KV Mini ($3) | $203/mo | 2x CX23, app + separate DB | ~$14/mo | ~15x |
Three honest readings. First, at hobby scale the multiplier nearly vanishes (~1.7x): a $12 Heroku setup is fairly priced, and migrating to save $5/month is a hobby in itself — stay.
Second, the multiplier is driven by per-dyno granularity, not usage: doubling Heroku RAM from 512 MB to 1 GB doubles the dyno price ($25 → $50 each) whether the app uses the headroom or not, while the flat box absorbs the same growth for $0 until you buy a second node. Third, the growing-prod rung is deliberately priced with Standard-0 Postgres and a separated database node on both sides — like-for-like-ish on HA posture for the data layer — and the gap still widens to ~15x, because Heroku's step functions compound (bigger dynos × more dynos + bigger database) while Hetzner's stays linear in node count.
Where Heroku still wins (and what the migration really costs)
If the post ended at the ladder, it would be an ad. So here is the counter-case the multiplier owes you: Heroku genuinely wins for any team that values zero ops over unit economics, and the price of the multiplier is paid in kinds of work the tables exclude.
What $87 (or $203) actually buys: multi-AZ dyno placement you never configure, managed Postgres with point-in-time recovery and one-command forks/followers, review apps per pull request with zero pipeline work, buildpacks that turn git push into a running service without a Dockerfile, and a compliance and SOC 2 surface you inherit instead of building. Reproducing even half of that on owned hardware means writing Dockerfiles, running pgBackRest or Postgres-operator backups to object storage and testing restores, wiring CI to build and push images, managing TLS and DNS, and owning the 3 AM page when the single Hetzner node does what single nodes do.
At contractor rates, one bad on-call month erases the $960 annual saving — which is why the hobby rung says stay, and why teams with no platform engineer should read the ~12x as "the discount for hiring ops," not free money.
The migration gotchas, concretely: buildpacks do not transfer — containerize the app first and run it locally. Postgres moves via pg_dump/pg_restore with a write-freeze window (or logical replication for near-zero downtime); extensions and collations must match the target version. Redis persistence (AOF/RDB) needs configuring on the new box — Heroku's managed restarts hid that from you. Scheduler jobs move to cron or a job runner. And Heroku's add-on ecosystem (logging, monitoring, error tracking drains) becomes a set of vendor accounts you now manage.
None of this is hard; all of it is real, and it is the actual line item the $87 was paying for.
The bottom line
A typical small-production Heroku stack costs $87/month at September 2026 list prices; the same containers on a post-repricing Hetzner CX23 cost ~$7/month — a ~12x gap (~7x with a separated database node, ~1.7x at hobby scale, ~15x as the stack grows). Egress, contrary to the folk version of this story, is $0 on both sides to 20 TB; the multiplier lives entirely in per-dyno compute granularity and managed-database tiers.
Hetzner's two 2026 price hikes narrowed the gap without closing it, and Heroku's bundled bandwidth plus zero-ops posture remain genuinely worth the premium for teams without platform capacity.
If that last sentence describes your team but the ladder still stings, the middle path is a self-hosted PaaS that keeps the git push workflow while billing you for flat hardware instead of per-dyno meters: declarative app resources on a Cluster-API fleet you own, with Postgres you control and bandwidth nobody meters.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



