finops-cloud-economics • 6 min read (1099 words)

Cloud Object Storage FinOps: AWS S3 vs. Cloudflare R2 vs. Backblaze B2 Rate-Card & Egress Analysis

Mathematical cost modeling of S3-compatible object storage across AWS S3 Standard, Cloudflare R2, and Backblaze B2 using published storage, request, and bandwidth rate cards.

Object storage cost breakdown comparing storage capacity vs outbound egress fees
Methodology: Derived mathematically from the official published pricing pages of Amazon S3 (us-east-1 Standard tier), Cloudflare R2, and Backblaze B2.

1. Deconstructing the Three Dimensions of S3 Object Storage Billing

S3-compatible object storage is the backbone of modern cloud applications—storing user-uploaded media, software release artifacts, container layers, database WAL backups, and AI training datasets. However, every S3-compatible bill is composed of three separate line items:
1. At-Rest Storage Capacity ($/GB-month): Charged based on average gigabytes stored over the billing month.
2. API Request Operations (Class A and Class B):
- Class A (Mutating / Listing Operations): PUT, COPY, POST, LIST, and multipart upload initiation.
- Class B (Read Operations): GET and HEAD object requests.
3. Outbound Internet Data Transfer (Egress $/GB): Charged whenever an object is downloaded over the public internet (or across cloud regions).

For read-heavy workloads—such as video/audio streaming, software distribution mirrors, public dataset repositories, or SaaS asset delivery where monthly downloads equal 2x to 10x the total stored data volume—outbound egress fees can represent 75% to 92% of the total storage bill on traditional hyperscalers.

2. Published Rate-Card Comparison: AWS S3 vs. Cloudflare R2 vs. Backblaze B2

The table below compiles the published standard rate cards from Amazon S3 (us-east-1), Cloudflare R2, and Backblaze B2:

| Billing Dimension | Amazon S3 Standard (us-east-1) | Cloudflare R2 | Backblaze B2 Reserve / Pay-As-You-Go |
| :--- | :--- | :--- | :--- |
| Storage Rate (First 50 TB) | $0.023 / GB-mo ($23.00 / TB) | $0.015 / GB-mo ($15.00 / TB) | $0.006 / GB-mo ($6.00 / TB) |
| Monthly Free Tier | 100 GB internet egress (account-wide) | 10 GB storage + 1M Class A + 10M Class B | 10 GB storage |
| Outbound Internet Egress | $0.09 / GB (first 10 TB)<br>$0.085 / GB (next 40 TB)<br>$0.070 / GB (next 100 TB) | $0.00 / GB ($0 Egress) | Free up to 3x average monthly stored volume (and $0 via Cloudflare CDN / Fastly Bandwidth Alliance), then $0.01 / GB ($10/TB) |
| Class A Operations (PUT/LIST) | $5.00 per 1,000,000 requests | $4.50 per 1,000,000 requests | Uploads (PUT) Free; LIST $0.004 per 1,000 |
| Class B Operations (GET/HEAD) | $0.40 per 1,000,000 requests | $0.36 per 1,000,000 requests | $0.004 per 10,000 ($0.40 per 1,000,000) |
| Delete Operations (DELETE) | Free | Free | Free |

3. Worked Mathematical Scenarios Across Three Workload Archetypes

Let us evaluate the exact monthly cost equation across three common engineering workloads (ignoring request fees when under 10M requests/month for clarity, or adding them explicitly where high):

Scenario A: Cold Backup & Disaster Recovery Vault (100 TB Stored, 2 TB Monthly Restore Egress)

When storing encrypted PostgreSQL pgBackRest archives and Proxmox VM snapshots that are rarely downloaded: - Amazon S3 Standard: $(100 ext{ TB} imes ext{tiered }pprox \$2,200) + (2,000 ext{ GB} imes \$0.09) = \$2,200 + \$180 = \mathbf{\$2,380 / ext{mo}}$ *(Note: AWS S3 Glacier Instant Retrieval reduces storage to $4/TB but charges higher retrieval fees).* - Cloudflare R2: $(100 imes \$15) + \$0 = \mathbf{\$1,500 / ext{mo}}$ - Backblaze B2: $(100 imes \$6) + \$0 ext{ (since }2 ext{ TB} \le 3 imes 100 ext{ TB)} = \mathbf{\$600 / ext{mo}}$

Winner for Low-Egress Archives: Backblaze B2 ($6/TB storage with 3x free monthly egress).

Scenario B: SaaS Media & AI Model Weights Delivery (50 TB Stored, 150 TB Monthly Egress, 50M GET Requests)

- Amazon S3 Standard: - Storage: $50 imes \$23 = \$1,150$ - Egress (150 TB tiered: 10 TB @ $90 + 40 TB @ $85 + 100 TB @ $70): $\$900 + \$3,400 + \$7,000 = \$11,300$ - 50M GET requests: $50 imes \$0.40 = \$20$ - Total AWS S3: $\mathbf{\$12,470 / ext{month}}$ - Cloudflare R2: - Storage: $50 imes \$15 = \$750$ - Egress: $\$0$ - 50M GET requests (first 10M free): $40 imes \$0.36 = \$14.40$ - Total Cloudflare R2: $\mathbf{\$764.40 / ext{month}}$ (93.8% reduction vs. uncached S3) - Backblaze B2: - Storage: $50 imes \$6 = \$300$ - Egress: $150 ext{ TB} \le 3 imes 50 ext{ TB} \implies \$0$ - 50M GET requests: $50 imes \$0.40 = \$20$ - Total Backblaze B2: $\mathbf{\$320.00 / ext{month}}$

Scenario C: High-Ratio Viral Distribution (10 TB Stored, 200 TB Monthly Egress)

When outbound transfer exceeds 3x stored volume (here, 20x stored volume): - Cloudflare R2: $(10 imes \$15) + \$0 ext{ egress} = \mathbf{\$150 / ext{month}}$ - Backblaze B2 (Direct Download): $(10 imes \$6) + ((200 - 30) ext{ TB} imes \$10/ ext{TB}) = \$60 + \$1,700 = \mathbf{\$1,760 / ext{month}}$ *(or $60/mo if fronted by Cloudflare CDN under the Bandwidth Alliance).*

4. Zero-Downtime Dual-Read Migration Strategy (AWS SDK v3)

Because both Cloudflare R2 and Backblaze B2 expose S3-compatible REST APIs (PutObject, GetObject, CreateMultipartUpload, and presigned URLs), you can migrate without downtime using a lazy read-through / dual-write proxy pattern:
1. Point new PutObject uploads to your R2/B2 bucket.
2. On GetObject, check R2/B2 first; if NoSuchKey (HTTP 404), fetch from legacy S3, stream to the client, and asynchronously copy the object into R2/B2.
3. Run rclone sync --transfers 32 in the background to backfill remaining historical keys.

Test your own storage-to-egress ratio interactively with our Object Storage & Egress Calculator.

5. Hidden Cost Trap: Small-Object Request Amplification (Class A / PUT & LIST Fees)

While Cloudflare R2 eliminates outbound bandwidth charges ($0.00/GB egress), its Class A (PUT, POST, COPY, LIST) request price is $4.50 per 1 million requests—similar to AWS S3's $5.00 per 1 million PUT requests.

How to Avoid Request-Fee Bill Shock on S3 and R2

If an observability pipeline (such as Loki, Thanos, Vector, or FluentBit) flushes tiny 4 KB log chunks to object storage every second across 200 pods, it generates over 500 million PUT requests per month (~$2,250+/month in request fees alone for less than 2 TB of data!):

1. Buffer and Compact Objects Before Upload: Configure log shippers and backup tools (restic, pgBackRest, Velero) to buffer chunks into 16 MB to 64 MB segments (or flush every 300 seconds) before issuing an S3 PUT or CreateMultipartUpload.
2. Set Lifecycle Rules to Abort Incomplete Multipart Uploads: Unfinished multipart uploads continue accruing monthly storage charges silently unless cleaned up automatically:

# Configure an S3/R2 lifecycle policy to abort incomplete multipart uploads after 2 days
aws s3api put-bucket-lifecycle-configuration \
  --bucket prod-media-assets \
  --lifecycle-configuration '{
    "Rules": [{
      "ID": "AbortIncompleteMultipartUploads",
      "Status": "Enabled",
      "Filter": {"Prefix": ""},
      "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 2}
    }]
  }'

Use our interactive Object Storage & Egress Calculator to model your exact storage volume and monthly egress ratio across AWS S3, Cloudflare R2, and Backblaze B2.

Official References