Skip to main content

R2 at $0, B2 at $0.01, S3 at $0.09: What Egress Really Costs Your Object Store

13 min readDora NodaDora Noda
Share

Serving 500 GB of objects shouldn't create a 127x spread in your bill. It does.

Put that 500 GB behind Cloudflare R2, Backblaze B2, and Amazon S3 for a month, push it out to users and CI runners at 10x the stored volume, and the egress line alone runs $0 on R2, $35 on B2, and $441 on S3 — a 127x gap on the same bytes, before you count storage or API calls. At 1x read volume the gap is even starker in absolute dollars: B2 and R2 charge nothing for the first 1.5 TB out, S3 charges $36 for the first 500 GB after its 100 GB free tier.

That spread is why "which bucket backs the PaaS" is no longer a rounding error. Tenant build artifacts, container layers, static assets, database backups, and user uploads all live in object storage. Every GET that leaves the bucket is a metered event on S3, a capped free allowance on B2, and a non-event on R2. The storage price per gigabyte gets the headline; egress decides the invoice.

This post recomputes the same PaaS-shaped workload across every meter — storage, operations, and egress — at three read ratios, then maps where a Cluster-API fleet should use a managed bucket and where it should stand up its own S3-compatible layer.


The Three Meters You Actually Pay

Object-storage vendors bill three things, not one. Comparisons that quote only the storage line miss the two that move first.

MeterCloudflare R2Backblaze B2AWS S3 StandardHetzner Object Storage
Storage / GB-month$0.015$0.00695 ($6/TB in docs, $6.95/TB billed)$0.023€0.00649 included in 1 TB bundle (€6.49/mo)
Egress to internet / GB$0.00Free to 3x avg stored, then $0.01$0.09 (first 10 TB, after 100 GB free)€0.001 (1 TB included, then €1/TB)
Class A ops (PUT, list) / 1M$4.50$5.00 (transactions)$5.00Free
Class B ops (GET, select) / 1M$0.36$0.40$0.40Free
IngressFreeFreeFreeFree
Free tier10 GB + 10M Class A + 10M Class B10 GB + 1 GB/day egress5 GB (12 months) + 100 GB egress/moNone — bundle starts at 1 TB

Numbers verified against vendor pricing pages as of July 2026.

Two footnotes matter before the math:

  • B2's 3x rule is on average stored, measured in byte-hours over the billing cycle. Store 500 GB on average and you get 1,500 GB of egress free that month. Average 100 GB and you get 300 GB free. The free egress resets monthly and is pooled across all buckets. Egress to Cloudflare, Fastly, and Vultr via the Bandwidth Alliance is unlimited and free regardless of the 3x cap.

  • S3's 100 GB free egress is shared across all AWS services and regions in the account, not per bucket. Transfer to CloudFront in the same region costs $0.02–0.06/GB instead of $0.09; same-region EC2/S3 is free. Every other internet egress dollar is at the headline rate until you cross 10 TB.

  • Hetzner's bundle (€6.49/mo for 1 TB storage + 1 TB egress + free API calls, then €8.70/TB storage and €1/TB egress overage) is priced in euros and raised in April 2026. Internal traffic between Hetzner Cloud and Object Storage in the same location is free.


The Core Recompute: One Workload, Five Bills

Take a representative PaaS object-storage workload — not a toy 10 GB bucket, not a data-lake outlier:

  • 500 GB stored on average (tenant uploads, build caches, image layers, nightly DB dumps)
  • 1M Class A ops (PUTs, multipart uploads, lifecycle transitions)
  • 10M Class B ops (GETs from app servers, CDN revalidations, CI pulls)
  • Egress varies — 1x, 3x, and 10x the stored volume in the sensitivity table below; the headline bill uses 500 GB egress (1x)

This is what a small-to-mid PaaS provision per cluster before any tenant traffic spike.

Line item (500 GB stored + 500 GB egress)Cloudflare R2Backblaze B2AWS S3 StandardHetzner Object StorageMinIO on Hetzner (self-hosted)
Storage$7.50$3.48$11.50€6.49 (~$7.00)~$5.40 (500 GB volume on CX)
Egress (500 GB)$0.00$0.00 (within 1,500 GB free)$36.00 (400 GB × $0.09 after 100 GB free)€0.00 (within 1 TB)$0.00 (20 TB included on Hetzner box)
Operations (1M A + 10M B)$8.10~$4.40$9.00$0.00$0.00
Monthly total$15.60$7.88$56.50~$7.00~$5.40
Egress share of bill0%0%64%0%0%

At 1x egress, the same bytes cost 7.2x more on S3 than on B2 and 3.6x more than on R2 — and that's the good ratio for S3. B2 is cheapest here because its per-gigabyte storage undercuts everyone and its free egress allowance still covers the whole 500 GB. R2 costs roughly double B2 on storage but wins on operational simplicity: zero egress math, ever.

Raise the read ratio and S3 pulls away — in the wrong direction:

Egress volume (same 500 GB stored)R2 totalB2 totalS3 totalHetzner Object totalSelf-hosted MinIO total
500 GB (1x)$15.60$7.88$56.50~$7.00~$5.40
1,500 GB (3x)$15.60$7.88 (still free)$146.50~$7.50~$5.40
5,000 GB (10x)$15.60$42.88 ($35 egress overage)$456.50~$11.00~$5.40
10,000 GB (20x)$15.60$92.88$906.50~$16.00~$5.40

R2's line is flat. B2 is flat to 3x, then climbs at $0.01/GB. S3 climbs at $0.09/GB from the first billable gigabyte. At 10x — a CDN-backed asset bucket or a CI system that pulls layers 20 times a day — S3 costs 29x R2 and 11x B2's overage-included bill for the same stored data.

The takeaway is not "R2 always wins." It's that egress is the only meter with a 127x vendor spread, so any workload where egress exceeds ~1x stored is egress-priced, not storage-priced. Most PaaS artifact and asset buckets live at 2–10x by design.


Where the Free Egress Actually Ends

Vendor marketing compresses egress into a single word — "free," "included," "3x." The breakpoints matter.

Cloudflare R2: free means free, with a caveat you pay elsewhere. R2 charges nothing for egress regardless of volume or destination. The trade is in Class A pricing ($4.50/M vs $0.40–0.36/M on B2/S3) and in ecosystem lock-in: R2 is cheapest when egress leaves through Cloudflare's network, and features like Object Lock, Cross-Region Replication, and fine-grained IAM arrived later than S3's. For a PaaS that already fronts assets through Cloudflare, R2's true cost is storage + ops; everything after that is margin the vendor chose not to meter.

Backblaze B2: 3x is generous until it isn't. At 500 GB stored, 1,500 GB of egress is free — enough for most artifact buckets. At 100 GB stored (a small tenant's uploads), only 300 GB is free and a single 500 GB traffic spike triggers the $0.01/GB overage. The blended overage rate is still 9x cheaper than S3, and B2-to-Cloudflare/Fastly egress bypasses the cap entirely. But teams that serve user-generated content directly from B2 without a CDN in front will hit the cap faster than they expect. B2's other hidden constraint is download transaction pricing: individual B2 downloads bill at Class B/C rates even when egress bytes are free.

AWS S3: $0.09/GB is the sticker; the real rate depends on where bytes go. 100 GB free per month is shared across every AWS service, not per bucket. S3-to-CloudFront is $0.02–0.06/GB. S3-to-EC2 in the same region is free. Internet egress to a Hetzner-hosted PaaS control plane is the full $0.09. The mitigation — CloudFront, S3 Transfer, or keeping consumers inside AWS — is itself a lock-in architecture. Every gigabyte that leaves AWS's edge is a dollar S3 could have kept if the consumer had stayed inside.

Hetzner Object Storage: a bundle, not a meter. At €6.49 for 1 TB/1 TB, Hetzner's object store is the cheapest managed S3-compatible option for a Hetzner-hosted fleet when data stays in-region. The bundle makes small buckets slightly wasteful (you pay for 1 TB even if you use 100 GB) and large buckets competitive only while egress stays under the included allowance. Overages are cheap (€1/TB egress), but Hetzner's regions are EU-only (FSN1, NBG1, HEL1) — a sovereignty win for EU tenants, a latency cost for global asset serving without an external CDN.


The Self-Hosted Line: When MinIO on Your Own Box Wins

A self-hosted PaaS has a fourth option no pricing page lists: run the S3 API itself.

MinIO (AGPL-3.0, commercial license if you offer it as a service), Garage, and SeaweedFS all expose an S3-compatible API on top of disks you already own. On a Hetzner Cloud box — a CX22 (€7.09/mo, 40 GB local NVMe) plus a 500 GB volume (~€5.40/mo), or a dedicated CCX/CPX box with local NVMe — the object store is a container next to your workloads, not a separate vendor invoice. Egress to tenants rides the box's 20 TB included monthly transfer. There is no per-gigabyte storage charge, no per-operation charge, and no egress meter beyond the Hetzner transfer allowance you'd already pay for the fleet.

That makes self-hosted the cheapest line at every row in the table above — ~$5.40/mo for 500 GB, flat, regardless of egress. So why not always self-host?

Three costs don't appear in the per-gigabyte math:

  1. Durability is your job. R2, B2, and S3 all promise 11 nines of durability via erasure coding and cross-AZ replication you don't operate. MinIO on a single Hetzner volume has exactly one copy on one provider's disk. Reaching comparable durability means running MinIO in distributed mode across 4+ nodes, or replicating with rclone/restic to a second provider — which adds hardware, cross-region bandwidth, and operational load the managed price already bundles.

  2. Operations are your pager. Managed buckets handle compaction, repair, TLS, access-key rotation, and versioning lifecycle. Self-hosted adds a stateful workload to your fleet's failure domain: disk-full alerts, quorum loss on rolling restarts, upgrade windows for the storage binary itself. For a PaaS team of two or three, that's a real on-call cost even if the dollar cost is zero.

  3. Egress durability cuts both ways. Self-hosted egress is "free" because it's already paid for in the Hetzner transfer allowance — until a traffic spike pushes the fleet's aggregate transfer over 20 TB and the provider throttles or bills overage. Managed object storage isolates that risk to the bucket; self-hosted pools it with every other service on the box.

The honest line: self-host wins outright for internal, high-churn, low-durability data — build caches, ephemeral CI artifacts, staging assets, and backups that already replicate elsewhere — where durability is "recreatable" and egress is bursty. Managed wins for tenant-facing, high-durability, globally-served data — user uploads, billing artifacts, and anything where "we lost a customer's file" is not an acceptable postmortem. Hetzner Object Storage sits in the middle: managed durability with Hetzner-local zero-egress, but without global edge delivery.

A practical split for a Cluster-API fleet:

Data classExampleRecommended backingWhy
Ephemeral / recreatableBuild caches, CI layers, preview assetsMinIO on fleet volumesFlat cost, high churn, recreatable if lost
Durable / EU-sovereignTenant uploads, invoices, backupsHetzner Object Storage or R2Managed durability, EU jurisdiction or zero-egress
Durable / bandwidth-heavyPublic assets, CDN origins, dataset downloadsR2 (or B2 via Cloudflare)Egress dominates; $0 / $0.01 beats $0.09 by 9–127x
Archive / coldAudit logs, old backupsB2Cheapest storage ($0.00695/GB), low egress

No single bucket is the answer. The PaaS's App spec can declare the backing store per volume, and the control plane can provision the right bucket type — self-hosted or managed — the same way it provisions node pools.


The Egress War Is a Pricing War, Not a Feature War

In 2024 Cloudflare's CEO called egress fees "the cloud's hidden tax." By 2026 the market has sorted into two camps with no middle ground.

Camp one — S3, GCS, Azure Blob — treats egress as a profit center. The per-gigabyte rate funds the network and keeps storage prices defensible. Every new bucket is another surface that can generate egress, and every transfer-acceleration or CDN upsell is a discount on a fee the vendor set. The price has not moved meaningfully in five years; the volume that creates it has multiplied.

Camp two — R2, B2 (with cap), Hetzner Object Storage (with bundle) — treats egress as a competitive weapon. R2's $0 detonated the model. B2's 3x-free-then-$0.01 halved the remaining gap. Hetzner's €1/TB overage is essentially zero compared to $0.09. The fast-growing story in 2026's "awesome object storage" comparisons is not a new durability number — it's how many workloads flipped from S3 to R2/B2 specifically to stop paying for bytes they already stored.

For a self-hosted PaaS, the war is good news twice over. First, the PaaS can pass through the cheaper egress vendors directly — R2 or B2 behind the tenant's app at no markup — instead of reselling S3 egress at a margin. Second, the PaaS's own infrastructure never pays the tax at all: inter-service traffic inside the Hetzner fleet, MinIO-to-app on the same VPC, and Hetzner Object Storage reads from Hetzner Cloud are all free. The "bandwidth is included" pitch that already wins the compute comparison wins the storage comparison for the same reason: the box's 20 TB transfer allowance is not per-service, per-bucket, or per-operation.

There are still reasons to pay S3's price — the deepest IAM, the widest tooling, and contractual durability that self-hosted can't match without staffing it. But "we've always used S3" is no longer a cost-neutral default. At 3x read volume the default costs 9x more than the cheapest managed alternative and 27x more than the box you already own. That's not a rounding error to revisit next quarter. It's a line item to provision differently today.


Running a fleet where storage and bandwidth are just there? Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. The App spec declares the backing store per volume, and the control plane provisions MinIO or a managed bucket the same way it provisions nodes under Cluster API. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex