Originally published on kuryzhev.cloud

A team keeps 40 TB of database dumps and file snapshots in S3, and the monthly bill looks fine until someone schedules a nightly restore into a staging environment on another cloud. Suddenly the line item that never mattered, data transfer out, is larger than storage. That is the situation where R2 vs S3 backups stops being a matter of taste and becomes arithmetic.

Short answer: if your backups are mostly write-once and rarely restored, and the data already lives on AWS, stay on S3 and tier it down with lifecycle rules. If you restore often, restore outside AWS, or serve backup data to several clouds or sites, R2's zero-egress model usually wins. Check three things before you move: where the data is written from, how many full restores you do per year, and whether you need S3-only features such as versioning or a specific compliance lock.

When the R2 vs S3 backups choice matters

Backup storage has an unusual cost shape. Writes are cheap, storage is the bulk of the bill, and reads are rare until the day they are urgent. Egress only dominates when reads stop being rare.

The patterns where it does:

  • Scheduled restore drills or nightly staging refreshes that pull full datasets.
  • Disaster recovery targets in a different cloud or on-prem, where restored bytes leave AWS.
  • Many consumers: analytics jobs, forensic tooling or partner sites reading the same backup set.
  • Large datasets with a short recovery time objective, where you cannot afford to throttle restores to save money.

Here is an illustrative calculation using list prices at the time of writing. Verify them on the AWS and Cloudflare pricing pages, because prices change and vary by region and tier. AWS internet egress starts at roughly $0.09/GB and is tiered, with lower rates above 10 TB per month. One full 50 TB restore out of S3 therefore lands somewhere around $4,000 to $4,500, versus $0 egress on R2. Storage costs differ too: around $0.023/GB-month for S3 Standard against around $0.015/GB-month for R2 Standard. That gap is small next to a handful of restores a year.

Watch out for the reverse direction. If the backup source is an EC2 instance, RDS export or EKS node, sending data to R2 means paying AWS egress on every upload. Zero egress on R2 does not erase the egress on the AWS side.

Option A: Amazon S3 (pros and cons)

S3 is the default for good reasons. It integrates with every backup tool, IAM, KMS, CloudTrail and AWS Backup, and it has the widest set of storage classes. The full feature list is in the Amazon S3 documentation.

Pros

  • Deep archive tiers (Glacier classes) make cold retention far cheaper per GB than any hot tier. For multi-year retention that is rarely read, this is hard to beat.
  • Versioning, Object Lock (including compliance mode), replication and cross-region copy are all native and well documented. That matters for ransomware-resistant designs.
  • Same-region transfer to EC2 and many other AWS services is free of per-GB transfer charges, so in-AWS restores from S3 Standard are cheap.
  • Data transfer in from outside AWS is free, so on-prem to S3 uploads carry no transfer fee. Request charges still apply.

Cons

  • Internet and cross-cloud egress is the expensive part, and it scales linearly with every restore and every drill.
  • Archive tiers add retrieval fees, minimum storage durations and, for the Flexible Retrieval and Deep Archive classes, restore delays. Those are fine for compliance copies but awkward for "restore now" scenarios.
  • Cost modeling is complicated: storage class, requests, retrieval, transfer and lifecycle transitions all bill separately.

Here is a lifecycle policy worth keeping for backup prefixes. It moves older dumps to Glacier Instant Retrieval, which is cheaper at rest but still readable in milliseconds. It expires the dumps later and aborts incomplete multipart uploads so orphaned parts stop billing. The day counts are examples, so set yours from your retention policy.

{ "Rules": [ { "ID": "backups-tiering", "Status": "Enabled", "Filter": { "Prefix": "pg/" }, "Transitions": [ { "Days": 30, "StorageClass": "GLACIER_IR" } ], "Expiration": { "Days": 400 }, "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 } } ] }

Save it as lifecycle.json and run aws s3api put-bucket-lifecycle-configuration --bucket YOUR_BUCKET --lifecycle-configuration file://lifecycle.json. Note that Glacier Instant Retrieval has a 90-day minimum storage duration and a per-GB retrieval fee. Restores from that class cost money even inside AWS.

Option B: Cloudflare R2 (pros and cons)

R2 exposes an S3-compatible API with no per-GB egress charge. Pricing and limits are in the Cloudflare R2 documentation.

Pros

  • No egress fees, so restore drills, multi-cloud DR and repeated reads from R2 Standard cost only storage and operations. The Infrequent Access class adds a per-GB retrieval fee and a minimum storage duration.
  • A simpler cost model: storage plus Class A (write/list) and Class B (read) operations, with an Infrequent Access class for colder data.
  • Works with rclone, restic, Kopia, the AWS CLI and SDKs by pointing at a custom endpoint.
  • Neutral ground: a backup copy outside your primary cloud account also limits blast radius if that account is compromised.

Cons

  • It is an S3-compatible subset, not full S3. At the time of writing, R2 does not offer S3-style object versioning. Retention is handled through R2 bucket lock rules rather than the S3 Object Lock API. Check the API compatibility page for every feature you rely on before designing around it.
  • There is no deep-archive tier at S3 Glacier Deep Archive prices. Very cold, multi-year data can be cheaper on S3.
  • Many small objects can make operation charges noticeable, because backup tools that chunk heavily generate lots of Class A requests.
  • Compliance teams may need documentation that R2's retention controls meet their requirement, whereas S3 Object Lock has long-established audit use.

This rclone remote and the checks after it get a backup target working. Replace the placeholders, and use an API token scoped to one bucket.

# ~/.config/rclone/rclone.conf # no_check_bucket is needed when the token cannot create buckets [r2] type = s3 provider = Cloudflare access_key_id = <R2_ACCESS_KEY_ID> secret_access_key = <R2_SECRET_ACCESS_KEY> region = auto endpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com acl = private no_check_bucket = true # upload, then verify rclone copy /var/backups/pg r2:backups-prod/pg --transfers 8 --checksum --progress rclone check /var/backups/pg r2:backups-prod/pg --one-way # expect: "0 differences found" rclone size r2:backups-prod/pg # object count and total bytes

Decision matrix

Score your own workload against these rows. Where several rows point the same way, that is your answer.

Situation Leans S3 Leans R2
Data produced inside AWS, restored inside AWS Yes: no upload egress, cheap restores No: you pay AWS egress on ingest
Data produced on-prem or in another cloud Fine Yes: free reads later
Full restores per year 0 to 1 Several, or scheduled drills
Restore target outside AWS Costly at scale Yes
Multi-year cold retention Yes: Glacier tiers Weaker
Need versioning or Object Lock compliance mode Yes No versioning; verify bucket lock fits
Account-isolated second copy Possible (separate AWS account) Yes: separate provider
Tooling depends on AWS Backup or KMS Yes No

Watch out for request costs. Backup tools that upload millions of small chunks can make R2 Class A operations or S3 PUT requests a visible cost. Raise chunk or pack sizes in restic or Kopia before comparing totals.

Evidence-based recommendation

No benchmark settles this. The documented pricing models do, so the call depends on your numbers. Estimate your yearly restore volume in GB and multiply it by your S3 egress rate, plus any retrieval fees for archive classes. If that figure, plus the storage difference, exceeds the cost of the migration effort and any lost features, choose R2. If it does not, S3 with lifecycle tiering is simpler.

For most teams the best answer is not either/or. Keep the primary backup on S3, where your workloads and compliance controls already are. Add R2 as an independent second copy if you need cross-cloud recovery or a provider-isolated copy. Populate it with rclone copy or your backup tool's second repository. Avoid rclone sync here, because it propagates deletions, and with them ransomware damage, to the second copy. This setup splits risk, and the cheap-to-read copy is the one you use for drills.

Do not choose R2 if you rely on S3 versioning, Object Lock compliance mode or deep-archive economics. Also skip it if your data originates in AWS and you restore rarely. Do not choose S3 as the only target if your DR plan restores outside AWS and nobody has priced a full restore.

Whichever you pick, test a full restore and record the time and cost. A backup you have not restored is a guess. More platform and cost-control write-ups live at kuryzhev.cloud.

Related