# Cut GCS Egress Costs: Migrate to Cloudflare R2

> Google Cloud Storage egress fees eating your budget? Migrate to Cloudflare R2 for zero egress costs. Step-by-step guide with rclone, real cost calculations, and S3-compatible SDK migration.

URL: https://www.matthewswong.com/en/blog/cloudflare-r2-gcs-egress/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2024-12-09
- Updated: 2024-12-09
- Category: DevOps

Google Cloud Storage egress fees are a silent budget killer. Every gigabyte of data your application reads from GCS and delivers to users costs money — $0.11/GB for the first 10 TB per month. At scale, this becomes one of the largest line items in your cloud bill. Cloudflare R2 solves this problem entirely: zero egress fees, S3-compatible API, and global distribution through Cloudflare's network. This guide walks through the real cost comparison, migration tooling, and application code changes needed to make the switch.

## The Real Cost of GCS Egress

Cloud storage pricing is deceptively simple on the surface — GCS Standard storage is $0.020/GB/month. But the egress costs hit hard: $0.11/GB for the first 10 TB exiting to the internet, $0.08/GB for the next 40 TB. A media platform storing 5 TB and serving 1 TB of video per month pays $20 for storage and $110 for egress — a 5.5x multiplier. Cloudflare R2 charges $0.015/GB for storage and literally $0 for egress, making the economics compelling for any read-heavy workload.

### Side-by-Side Cost Comparison

The numbers speak clearly when you lay them side by side. At 100 GB egress per month, GCS costs $31/month versus R2 at $15/month — a 52% saving. At 1 TB egress, GCS costs $131/month versus R2 at $15/month — an 89% saving. The higher your egress volume, the more dramatic the savings. The break-even point where R2's storage price difference ($0.005/GB more expensive) is offset by egress savings is at roughly 3 GB of egress per 1 GB stored.

## Migrating Data with rclone

rclone is the industry-standard tool for cloud storage migrations. It understands dozens of storage backends including GCS and Cloudflare R2 (via the S3-compatible API), can sync in parallel with multiple transfer threads, and supports dry-run mode for safe testing. The migration is a two-step process: first sync all data from GCS to R2, then update your application configuration to point at R2, then run a final sync to catch any objects written during the cutover window.

### Step-by-Step Migration Script

Configure rclone with both a GCS remote (using your service account JSON) and an R2 remote (using R2 API tokens from the Cloudflare dashboard). Always run with --dry-run first to verify the object count and total size matches your expectations. The --transfers 32 flag parallelizes the upload across 32 concurrent threads — adjust based on your network bandwidth. Large migrations (>100 GB) are best run from a VM in the same region as your GCS bucket to minimize transfer costs during migration.

## Updating Your Application Code

Because R2 is S3-compatible, migrating your application code is often just changing the endpoint URL and credentials. If you're using the AWS SDK (@aws-sdk/client-s3), you only need to update the S3Client constructor — all the GetObject, PutObject, DeleteObject, and ListObjects calls remain identical. If you're using the GCS client library directly, you'll need to swap to the AWS SDK or use an S3-compatible library like MinIO client.

### SDK Migration: GCS Client to R2 via S3 API

The AWS SDK for JavaScript, Python, Go, and other languages all support custom endpoints — just point them at your R2 endpoint (https://'{account_id}'.r2.cloudflarestorage.com) with your R2 API token credentials. The region should be set to 'auto' as R2 automatically routes to the nearest Cloudflare datacenter. For public assets, enable R2's public bucket feature to serve files directly from a custom domain through Cloudflare's CDN at zero egress cost.

> During migration, enable GCS Object Change Notifications or Pub/Sub to capture any new objects written during the migration window. Write them to both GCS and R2 simultaneously (dual-write pattern) to ensure zero data loss during cutover. Only remove GCS after verifying R2 has all objects via rclone check --size-only.

> **R2 Does Not Support All GCS Features**

> Cloudflare R2 is S3-compatible, not GCS-compatible. Features specific to GCS — like Object Lifecycle Management (equivalent to S3 Lifecycle), Uniform Bucket-Level Access, and IAM Conditions — need to be replicated using R2 equivalents or Cloudflare Workers. Audit your GCS configuration before migrating to ensure all features you rely on have R2 equivalents.

## R2 Advanced Features and Key Terminology

After migrating to R2, explore these features to further reduce costs and improve performance: R2 Event Notifications trigger Cloudflare Workers on object changes — useful for image processing pipelines. R2 Object Lifecycles automatically expire or transition objects to reduce storage costs. Cloudflare's Cache API can cache R2 objects at edge for the hottest content, reducing even the R2 operation count. Combined with Workers for on-the-fly image resizing, R2 can replace a CDN + object storage + image processing stack at a fraction of the cost.

## Source links

- https://developers.cloudflare.com/r2/
- https://rclone.org/s3/
- https://cloud.google.com/storage/pricing

## FAQ

### Why are Cloudflare R2 egress costs so much lower than Google Cloud Storage?

Google Cloud Storage charges $0.11/GB for the first 10 TB of egress to the internet each month, while Cloudflare R2 charges literally $0 for egress. This is because Cloudflare's business model is built around network services, so they absorb egress costs rather than passing them to customers. For read-heavy workloads, this difference can reduce storage bills by 52–89% depending on egress volume.

### What tool does the post recommend for migrating data from GCS to R2, and what precautions should you take?

The post recommends rclone, the industry-standard tool for cloud storage migrations. You should always run it with the --dry-run flag first to verify object count and total size before the real sync. Large migrations over 100 GB are best run from a VM in the same region as your GCS bucket to avoid incurring transfer costs during the migration itself.

### How much application code change is required to switch from GCS to R2?

Because R2 is S3-compatible, the change is often minimal — if you are already using the AWS SDK (@aws-sdk/client-s3), you only need to update the S3Client constructor with the R2 endpoint and credentials. All GetObject, PutObject, DeleteObject, and ListObjects calls remain identical. If you use the native GCS client library directly, you will need to swap to the AWS SDK or an S3-compatible library such as MinIO client.

### How do you ensure zero data loss during the cutover from GCS to R2?

The post recommends a dual-write pattern: enable GCS Object Change Notifications or Pub/Sub to capture new objects written during the migration window and write them to both GCS and R2 simultaneously. After the initial sync, run a final rclone sync to catch any remaining new objects, then verify R2 has all objects using rclone check --size-only before decommissioning GCS.

### Are there GCS features that R2 does not support?

Yes. R2 is S3-compatible, not GCS-compatible, so GCS-specific features like Object Lifecycle Management, Uniform Bucket-Level Access, and IAM Conditions are not directly available. These need to be replicated using R2 equivalents or Cloudflare Workers. The post advises auditing your GCS configuration before migrating to confirm every feature you rely on has an R2 equivalent.

---

- HTML version: https://www.matthewswong.com/en/blog/cloudflare-r2-gcs-egress/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/cloudflare-r2-gcs-egress/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
