Your app served 500 GB of images, video, and API responses last month. On one provider that line item is $0. On another it is $60. Same bytes, same internet, 127 times more expensive if you picked the wrong logo for your invoice — and the spread is even wider once free tiers, regional multipliers, and the "zero egress" fine print are counted.
This is not a rounding error. Egress — every byte your app sends back to a browser — is the widest single line item in a 2026 cloud bill. Compute has converged. Storage has converged. Bandwidth has not. The gap between the cheapest and most expensive way to ship a gigabyte to the public internet still spans more than two orders of magnitude, and every PaaS, serverless platform, and hyperscaler draws its line in a different place.
Here is the upfront answer for a media-heavy app moving 500 GB/month of outbound traffic (a modest gallery, a product catalog with real images, or a single popular video embedding):
| Provider | Egress rate (internet) | Free tier | You pay for 500 GB | Notes |
|---|---|---|---|---|
| Cloudflare R2 / Workers | $0.00/GB | 10 GB storage free; egress always zero | $0.00 | Zero egress is the product. Ops and storage still bill. |
| Hetzner Cloud (EU, CX22) | €1.00/TB overage ≈ $0.001/GB | 20 TB per server | $0.00 | Entire 500 GB rides inside one server's allotment. |
| Fly.io (North America / Europe) | $0.02/GB | 100 GB free (NA/EU) | $8.00 | (500 − 100) × $0.02 |
| Fly.io (Asia-Pacific / South Am.) | $0.04/GB | 30 GB free | $18.80 | (500 − 30) × $0.04 |
| Fly.io (Africa / India) | $0.12/GB | 30 GB free | $56.40 | (500 − 30) × $0.12 |
| GCP (Premium Tier) | $0.08–$0.12/GB | 1 GB free | ~$42–60 | First TB at $0.12, then $0.11; blended ~$0.11 at 500 GB |
| Azure Blob (Hot) | $0.087/GB | 5 GB free | ~$43.07 | (500 − 5) × $0.087 |
| AWS S3 (Standard) | $0.09/GB | 100 GB free (since 2024) | $36.00 | (500 − 100) × $0.09; $0.085 next tier irrelevant at this scale |
| Backblaze B2 | $0.01/GB | 1 GB/day free | ~$4–5 | Cheapest hyperscaler-adjacent; 3× free via Cloudflare |
The headline spread — $0 to $56.40 — is a single workload at a single volume, and the factors behind it compound fast. The rest of this post is where that 127x number comes from, what the bill does at five different scale points, and why even Cloudflare's $0 line has a lock-in cost that a self-hosted S3 on the same fleet you already run is built to avoid.
Why egress is the one line item that never converged
Ten years ago every cloud charged for bandwidth. Then Cloudflare made zero egress a product pillar for Workers, R2, and its CDN: no per-gigabyte tax on bytes leaving the network, ever. The hyperscalers did the opposite. They kept egress as a margin lever while cutting compute and storage — because bandwidth is the one line a customer cannot optimize away without changing where the data lives. You can compress an image. You cannot compress away the need to send it.
The result is a market where the same gigabyte is priced five different ways at once:
- $0.00 — Cloudflare R2, Workers, and any traffic that stays on Cloudflare's network. This is intentional: Cloudflare peered its way out of paying transit costs at hyperscaler rates and passes the arbitrage to you.
- ~$0.001/GB — Hetzner EU's overage rate: €1 per terabyte after the 20 TB that ships with every CX22-class server. Even overage is two orders below AWS. In Hetzner's US and Singapore regions the included allotment drops to 1 TB per server, still far above a PaaS free tier, with overage at €1/TB (EU/US) or €7.40/TB (SG).
- $0.02–$0.12/GB — Fly.io, split into three regional buckets. North America and Europe cost $0.02, Asia-Pacific/Oceania/South America $0.04, Africa and India $0.12. The cheapest Fly region is already 20 times Hetzner's overage; the most expensive is 120 times.
- $0.087–$0.12/GB — the hyperscaler band. AWS $0.09, Azure $0.087, GCP Premium $0.08–$0.12 depending on tier and region. Tiers step down at 10 TB and 40 TB, but at 500 GB you are on the top rate. Cross-region and cross-AZ traffic adds $0.01–$0.02/GB on top, billed separately from internet egress.
- Free tiers that look generous until you do the division. AWS gives 100 GB/month free (expanded in 2024), Azure 5 GB, GCP 1 GB, Fly 100 GB in NA/EU but only 30 GB in Africa/India. At 500 GB, the free tier covers 20% of the bill on AWS, 6% on Fly Africa, and 0.2% on GCP.
That five-way split is what the "127x" shorthand tries to capture. Literal zero makes a ratio infinite, so the honest comparison is the cheapest non-zero metered rate you actually hit versus the most expensive: Fly Africa's $0.12 versus Hetzner's ~$0.00095/GB overage is roughly 126:1 — or, counting the other direction, the same 500 GB costs $0 on Cloudflare and $60 on Fly Africa for the identical payload. Pick the pair that stings and the math still lands above 100x. The marketing page never shows you that pair.
Two footnotes engineers miss on first read:
- Hetzner counts per server, not per account. A three-node fleet in EU has 60 TB of included transfer pooled across the servers. One app that pushes 500 GB has not even scratched 1% of it.
- Fly counts per region group, not per app. An app with users in both Berlin and Johannesburg pays two different egress rates for the same bytes, with different free allowances. Run the same workload from
fravsjnband the invoice doubles or triples without a config change.
Line by line: what each provider actually charges for your 500 GB
Cloudflare: $0 egress, but not $0 storage, ops, or lock-in
R2's pricing in 2026 is three lines: $0.015/GB-month storage, $4.50 per million Class A (writes), $0.36 per million Class B (reads), and $0 for egress. Workers and the CDN share the same principle — bytes out do not bill. The free tier is 10 GB stored, 1M writes, 10M reads per month.
For the 500 GB egress scenario, the egress line is cleanly zero. What does bill is everything else: storing the objects behind those 500 GB (say 100 GB of source images at $1.50), plus the read ops to serve them. A gallery that serves 500 GB as 200,000 image requests of ~2.5 MB each pays roughly $0.07 in Class B ops — noise. A thumbnail API that serves the same 500 GB as 5 million 100 KB hits pays $1.80 in reads. Cloudflare wins every bandwidth-heavy comparison on egress alone; the trap is not on this page but in the next section, where the data has to live in R2 to get the zero rate.
Hyperscalers: $36–$60 at 500 GB, and the tiers only help at terabytes
- AWS S3 Standard (us-east-1): $0.023/GB-month storage, $0.09/GB egress for the first 10 TB, then $0.085, $0.07, $0.05 in steps. Free tier: 100 GB egress/month. So 500 GB bills as (500 − 100) × $0.09 = $36.00 on egress alone. Add ~$2.30 to store 100 GB and ~$0.80 in GET requests. At 50 TB the blended egress rate drops meaningfully; at 500 GB the headline rate is the only rate.
- Azure Blob Hot: $0.018/GB-month storage, $0.087/GB egress for Zone 1 (NA/EU). Free tier: 5 GB. So (500 − 5) × $0.087 = $43.07. Cross-AZ replication traffic is a separate meter.
- GCP Cloud Storage Standard: $0.020/GB-month storage, Premium Tier egress $0.12/GB for the first TB, $0.11 to 10 TB. At 500 GB you are inside the first TB, so the blended rate is ~$0.12. After the 1 GB free tier, 499 × $0.12 = ~$59.88 before Cloud CDN discounts. Even at GCP's cheaper Standard Tier ($0.085), the same 500 GB is ~$42. The GCP-to-AWS delta at 500 GB is $6–24 every month for the same bytes.
A detail that 2026 teardowns keep flagging: NAT Gateway and inter-AZ traffic are separate lines. AWS charges $0.045/GB through a NAT Gateway and $0.01/GB each direction for cross-AZ. A three-AZ VPC that seemed to have one egress bill can have three egress-adjacent bills. None of these appear on the pricing page you compared when you picked the provider for its $0.09 number.
Fly.io: three prices for one platform
Fly's 2026 pricing page splits egress into three region groups — a rare honest presentation of a global cost surface:
| Region group | Price | Free | Your 500 GB bill |
|---|---|---|---|
| North America + Europe | $0.02/GB | 100 GB | $8.00 |
| Asia-Pacific + Oceania + South America | $0.04/GB | 30 GB | $18.80 |
Africa + India (jnb, bom) | $0.12/GB | 30 GB | $56.40 |
The 6:1 spread inside a single provider catches teams by surprise. A startup that launches in iad and adds bom for Indian users without re-reading the pricing page watches its per-gigabyte cost triple overnight — same fly deploy, different line on the invoice. Fly's private-network cross-region traffic also bills ($0.006–$0.05/GB), so a multi-region Postgres or LiteFS cluster adds a meter that Hetzner customers never see because inter-DC traffic inside a Hetzner project is not metered the same way.
Hetzner: the flat-rate counterpoint
A Hetzner CX22 in Nuremberg, Falkenstein, or Helsinki ships with 20 TB of outbound transfer and inbound is always free. Overage is €1/TB. A CX22 in Ashburn or Singapore ships with 1 TB included at the same overage (Singapore €7.40/TB). In either EU case, 500 GB is 2.5% of one server's allotment.
Translated to headline rates:
- Inside the allotment: effective $0.00/GB for your workload. Not "free tier" — a flat 20 TB you already paid for at €3.79/month for the box.
- Overage: €1/TB = €0.001/GB ≈ $0.00117/GB at 2026 EUR/USD. Five hundred gigabytes of overage would cost about $0.58 — less than a single AWS GET-request line.
The pooled math matters for a fleet. Two CX22s in EU pool 40 TB. Five pool 100 TB. The app that scaled from 500 GB to 5 TB did not incur a new bandwidth bill; it just consumed 12.5% instead of 1.25% of what the hardware already includes. This is the core self-hosting argument in one number: the bandwidth is not a meter that grows with usage, it is capacity you bought when you rented the box.
Sensitivity check: what changes at 50 GB, 2 TB, and 10 TB
The title promised 500 GB, but a real app's traffic moves. The honest way to show whether the "cheapest at 500 GB" stays cheapest at 50 GB and 10 TB is to recompute the same workload at five points, using the same free-tier rules and no hidden volume discounts except the hyperscalers' published tier steps:
| Monthly egress | Cloudflare R2 | Hetzner EU (CX22) | Fly NA | Fly Africa/India | AWS (with free tier) | GCP Premium |
|---|---|---|---|---|---|---|
| 50 GB | $0.00 | $0.00 | $0.00 (inside 100 GB free) | $2.40 | $0.00 (inside 100 GB free) | ~$5.88 |
| 500 GB | $0.00 | $0.00 | $8.00 | $56.40 | $36.00 | ~$59.88 |
| 2 TB (2,048 GB) | $0.00 | $0.00 | $38.96 | $242.16 | $175.32 | ~$236.16 |
| 5 TB (5,120 GB) | $0.00 | $0.00 | $100.40 | $610.80 | $451.80 | ~$578.40 |
| 10 TB (10,240 GB) | $0.00 | $0.00 | $202.80 | $1,225.20 | $914.40 | ~$1,144.80 |
| 20 TB (20,480 GB) | $0.00 | $0.00 (exactly the allotment) | $407.60 | $2,454.00 | ~$1,756.80 | ~$2,109.60 |
Read that table row by row:
- At 50 GB, Fly NA and AWS both sit inside their free tiers — zero egress bill, just like Cloudflare. This is the workload where "egress doesn't matter" is actually true and platform choice should be made on other dimensions.
- At 500 GB the free tier is exhausted and meters diverge. Fly NA at $8 is the cheapest metered line; Fly Africa already costs more than AWS.
- At 2 TB and above, Hetzner's flat rate is the only line that stays at zero. AWS and GCP charge $175–$236 at 2 TB, Fly Africa $242. At 20 TB — one server's full allotment — the hyperscaler bill is $1,700–$2,400 while Hetzner still bills $0 and Cloudflare still bills $0 on egress. The gap widens with volume; it never narrows.
Two caveats that keep this honest:
- Cloudflare's $0 row is egress only. A 10 TB R2 bucket still bills ~$150/month in storage before egress. Hetzner's $0 row is also egress only — you still pay €3.79 per server. But neither charge grows when outbound doubles, which is the structural difference versus every per-gigabyte meter.
- GCP's first-terabyte-at-$0.12 rule makes it the most expensive hyperscaler below 1 TB and merely tied above it. Small image-heavy apps hit the worst GCP rate first.
The R2 catch: zero egress still leaves a lock-in surface
Cloudflare earned the zero-egress line item. It is the cheapest way to serve bytes to the internet, period. The trap is not in the price but in the condition: you only get $0 if the bytes live in R2 and leave through Cloudflare's network.
Move the data out, and you pay the inverse: R2 charges for the egress it otherwise waived if you replicate away, and every Worker, Queue, D1, or Durable Object that touches that data is billed on Cloudflare's own metering (Durable Objects storage billed separately since January 2026, Workflows step/storage since August 2026). The bucket is not a disk; it is a surface area.
Three concrete forms the lock-in takes:
- API surface. R2 is S3-compatible but not S3-identical. Lifecycle rules, replication, and event notifications have Cloudflare-shaped edges. Tooling that assumes native S3 (cross-region replication, S3 Select, certain IAM policies) needs adaptation. The migration off R2 is not "flip the endpoint" when the app grew Cloudflare-specific glue.
- Network gravity. Zero egress is a pull toward Workers, Pages, and Cloudflare's CDN. That gravity is a feature when you want Cloudflare's edge — and a cost when you need a workload that Cloudflare does not run well (long-lived Postgres, stateful queues on your own wire, GPUs). The moment compute must live elsewhere, the $0 egress stops being zero because the data path is no longer R2 → Cloudflare eyeball.
- Bucket as control plane. Teams that adopted R2 to dodge S3 egress still hand Cloudflare the same centralized control over their data path they were trying to avoid with AWS — different logo, same single-vendor chokepoint. The January 2026 multi-hour R2/Durable Objects incident is the recent reminder that "zero egress" and "zero operational risk" never meant the same thing.
This is where the TODO's "honest alternative" lands: a self-hosted S3-compatible layer on the fleet you already own. Garage, MinIO (pgsty/minio fork), SeaweedFS, or Hetzner Object Storage (S3-compatible, 1 TB included per project at ~€5/TB-month after) all speak the same S3 API without a hyperscaler or Cloudflare account in the path. The trade is explicit:
- You pay for the disk and the bandwidth you already bought (Hetzner 20 TB/server; garage on local NVMe has no per-GB meter at all) instead of paying a per-gigabyte tax to leave someone else's network.
- You carry the operations: replication, erasure coding, upgrades, and backups are yours. ElastiCache-for-Valkey teams learned this calculus in H1 2026 — managed Valkey at $0.90/GB-month versus self-hosted Valkey on Hetzner where the RAM is yours and the bill is flat. Object storage makes the same trade.
- You keep endpoint portability.
AWS_ENDPOINT_URL,R2_ACCOUNT_ID.r2.cloudflarestorage.com,garage.example.internal:3900— the app sees one S3 client and the operator picks where bytes live per bucket. That is the lock-in hedge R2 alone cannot be.
For the media-heavy app in this post, the practical shape is often both: serve hot images and video through R2 or a CDN with zero egress, and keep the origin bucket and long-tail archive on a self-hosted Garage/MinIO fleet that never bills per gigabyte to read. Bex makes that split boring — bex.yml can point builds at either S3 endpoint and the fleet's storage is just another service on the same Cluster API machines that already run the app, not a second cloud account with a second egress meter.
Choosing without pretending egress is free anywhere
No provider makes bandwidth truly free. Cloudflare amortizes it into peering and bundles it into a platform bet. Hetzner amortizes it into 20 TB of server capacity. The hyperscalers itemize it so it shows up as the line that grows fastest.
Use this decision frame for the next app that will ship images or video at volume:
- Below ~100 GB/month, egress price is noise. AWS's free tier, Fly NA's free tier, Cloudflare's free tier, and Hetzner's allotment all read $0. Choose on latency, DX, and where your database lives.
- At 500 GB–2 TB — the band most indie and mid-traffic apps live in — the gap is already $0 vs $36 vs $56. One month at 500 GB pays for a full Hetzner CX22 for seven years at the AWS delta. This is the band where the egress recompute belongs in the launch checklist, not the post-mortem.
- Above 5 TB, flat-rate wins outright unless there is a specific reason to pay per gigabyte (global edge, regulated data residency that a single provider offers). The flat-rate trade is operational — you run the storage layer — not financial.
- If you use Cloudflare for zero egress, keep the S3 endpoint portable. Ship with an S3 client that can point at R2 today and Garage/MinIO tomorrow. The eight-character env var that switches endpoints is the cheapest insurance against any single provider's next incident, repricing, or API drift.
The single number to carry out of this post: the cheapest way to ship a gigabyte and the most expensive differ by more than a hundred to one — $0.001 versus $0.12 — for the same bytes on the same internet. Egress is the line item that rewards asking where the data lives before the first deploy, not after the first surprise invoice. The team that draws that boundary around hardware it owns, on a fleet whose bandwidth is capacity rather than a meter, is the team that never has to explain to finance why last month's gallery cost more than last month's servers.
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.