migrate s3 to r2s3 to r2 migrationcloudflare r2 migrationmove data from s3 to r2

Moving off S3 to Cloudflare R2: the honest guide

September 2026 · 10 min read · Mayur

Moving off S3 to Cloudflare R2: the honest guide

Most migration guides are written by someone who has never done the migration. You can tell, because they stop at "run the copy command" and never mention the part where you sit there afterwards wondering whether it actually worked.

This one covers the whole thing, including the parts that go wrong.

Why people make this move

One reason, mostly: egress.

AWS charges roughly $0.09 per gigabyte to send your data out to the internet, after a 100 GB monthly free allowance. Cloudflare R2 charges nothing for egress. If you serve files to users, that difference is not a rounding error, it is often the largest line on the bill.

The storage rates are also different, though less dramatically. S3 Standard sits around $0.023 per GB per month. R2 is around $0.015. On 10 TB that is roughly $235 versus $150, so about $85 a month before you count a single byte of traffic.

The egress is the real prize. If you are serving 5 TB a month to users from S3, you are paying somewhere around $450 a month for that traffic alone. On R2 that line is zero.

When this move is not worth it. If your data lives entirely inside AWS and only moves between S3 and EC2 in the same region, you are not paying internet egress rates and this whole exercise saves you very little. If you depend on S3 features R2 does not have (more on that below), the move is not a migration, it is a rewrite. And if you are moving less than a terabyte, the saving is real but small, and your time is worth more.

The gotchas, which is the actual reason to read this

This is the part other guides skip. Every one of these has cost somebody a weekend.

1. Multipart uploads break ETag comparison

This is the big one and it catches nearly everybody.

The usual way to verify a copy worked is to compare ETags, because for a simple upload the ETag is the MD5 of the object. For a multipart upload it is not. It is a hash of the concatenated part hashes, with a dash and the part count appended, like d41d8cd98f00b204e9800998ecf8427e-14.

Two consequences. First, an object uploaded in multiple parts on S3 will almost never have a matching ETag on R2, because the part boundaries differ. Second, if you verify by comparing ETags, you will get a wall of mismatches on large files and conclude the migration failed when it did not.

What to do: verify by size and object count for multipart objects, and use ETag comparison only for objects under your multipart threshold. If you need genuine content verification on large objects, compute checksums yourself on both sides. Do not trust a tool that reports "verified" without telling you which method it used.

2. Storage classes do not map

S3 has Standard, Standard-IA, One Zone-IA, Intelligent-Tiering, Glacier Instant Retrieval, Glacier Flexible Retrieval, and Deep Archive. R2 has one storage class, plus an infrequent access tier.

If your data is in Glacier, you cannot just copy it. You have to restore it first, which takes minutes to hours depending on tier, and costs a retrieval fee. Deep Archive restores can take up to 12 hours. Plan for this and start it days before you plan to migrate, or you will discover it at the worst moment.

3. Object metadata and ACLs

R2 does not implement S3 ACLs. If you relied on per-object ACLs for access control, that logic has to move to bucket-level policy or to Cloudflare's own access controls. Custom metadata copies fine. Content-Type copies fine and matters more than people expect, because getting it wrong means browsers download files instead of displaying them.

4. The exit fee is real and you pay it once

Moving 10 TB out of S3 costs roughly $911 in transfer fees. That is AWS's charge for the data leaving, and no tool avoids it, because it is billed by AWS to you.

What you can avoid is a second fee on top. Several migration tools charge per gigabyte for the transfer itself. Movebot charges $0.75 per gigabyte, so 10 TB is $7,500 in tooling on top of the $911 you already owe AWS. Check what your tool charges before you start, not after.

The exception worth knowing about: Cloudflare's own Super Slurper pulls data into R2 from S3 on their infrastructure, and they do not charge for it. If your destination is R2, that is the cheapest route in existence and you should know it exists whether or not you use our product.

5. Rate limits and the long tail

A bucket with 50 million small objects is a completely different problem from a bucket with 50,000 large ones. The bytes are not the constraint, the request count is. Expect throttling, make sure whatever you use resumes properly, and test on a prefix before you run the whole thing.

6. R2 does not have everything

No S3 Object Lock at the time of writing. No lifecycle transitions between classes in the S3 sense. If you need WORM for a compliance requirement, R2 is not your destination and you should find that out now rather than after the data has moved.

The actual migration

Before you start

  1. Inventory. Object count, total size, size distribution, and how many are in Glacier classes. aws s3 ls --recursive --summarize gets you the basics.
  2. Restore anything archived. Start this first. It is the longest lead time in the whole project.
  3. Create the R2 bucket and generate an S3-compatible API token. R2 speaks the S3 API, which is why any S3 tool works against it.
  4. Test on one prefix. Pick a few hundred objects. Move them. Verify them. Read one back through your application. Do not skip this.
  5. Write down what "done" means before you start, so you are not deciding under pressure at 1am.

The move

Option A: Cloudflare Super Slurper. If the source is S3 and the destination is R2, this is the cheapest path. Cloudflare runs it, and they do not charge for the transfer. It is a bulk import, so it is best for a one-time move rather than an ongoing sync.

Option B: rclone. Free and genuinely good.

rclone copy s3:source-bucket r2:dest-bucket \
  --transfers 32 --checkers 64 --fast-list --progress

Tune --transfers to your bandwidth. Use copy, not sync, until you are confident, because sync deletes things at the destination that are not at the source, and that command has ruined people's weekends. You will need a machine that stays awake for the duration, and you will need to remember these flags again next time.

Option C: a console that does it for you. This is what we built. You connect both providers, pick source and destination, and the job runs server-side. It resumes if the connection drops, it reports what completed, and if the destination is R2 it can use Cloudflare's own fast paths so the bytes stay free. We do not charge per gigabyte.

All three move the same bytes. The difference is entirely in what happens around the transfer.

Verifying it actually worked

The step everyone rushes and then regrets.

  1. Object count matches. The cheapest check and it catches most failures.
  2. Total size matches. Catches truncation.
  3. Spot-check content, especially large multipart objects, by downloading a sample from both sides and comparing bytes rather than ETags.
  4. Test through your application, not just the storage API. Content-Type problems only appear here.
  5. Wait before deleting the source. A week minimum. The whole point of having done the migration is that you still have the original if you got it wrong. Deleting early to save one month of storage is the most expensive saving available.

Cutting over

Update endpoints, keep both live in parallel for a few days, watch error rates, then decommission. If you can run a scheduled sync from S3 to R2 during the parallel period, you get a much calmer cutover, because the destination stays current while you gain confidence.

The honest bit

rclone will do this for free and it will do it well. If it is just you, you are comfortable in a terminal, and you can leave a machine running, you may not need any product for this. I would rather say that than pretend otherwise.

What you are paying for, if you pay anyone, is the part around the transfer: a schedule that runs without a server you maintain, a record that the job finished, a teammate who can see the same status you can, and not having to remember the flags in eight months when you do this again.

R2 is not strictly better than S3. It is cheaper for egress-heavy workloads and it lacks features S3 has. If you need Object Lock, deep archival tiers, or the wider AWS integration surface, staying is the correct engineering decision and the cheaper bill is not worth the rewrite.

Migrations are boring and they mostly go fine. The reason this article is long is that the 5% that goes wrong goes wrong in the same six ways every time, and all of them are avoidable if you know about them beforehand.

Questions people actually ask

How long does it take to migrate 10 TB from S3 to R2? Hours to a couple of days, depending on connection, object count, and concurrency. Object count matters more than total size. A million small files takes longer than the same bytes in a thousand large ones.

Will my URLs change? Yes, unless you put a custom domain in front of R2, which you should. Cloudflare makes this straightforward and it means you never have to do this again.

Can I keep S3 and R2 in sync during the transition? Yes, and you should during cutover. Either a scheduled sync, or R2's Sippy, which copies objects on read so the migration happens gradually as traffic arrives.

Does R2 support the S3 API? Yes, which is why every S3 tool works with it. That compatibility is the reason this migration is a copy operation rather than a rewrite.

What about Glacier data? Restore it to a retrievable state first. This takes minutes to hours depending on tier, costs a retrieval fee, and is the longest lead time in the project. Start it before anything else.

Is Super Slurper better than rclone? For a one-time bulk import into R2, usually yes, because Cloudflare runs it and does not charge for the transfer. For ongoing sync, no, because it is an import tool rather than a sync tool.

What if I want to move back? You can. R2 does not charge egress, so moving out of R2 is cheaper than moving out of S3 was. That asymmetry is worth noticing.


If you want both providers in one console, with the migration running server-side and no per-gigabyte charge, that is what Storafleet does. Your data stays in your own accounts. Connect a bucket, free, no card.


Internal links

/blog/what-s3-egress-actually-costs · /blog/what-nobody-tells-you-about-migrations · /blog/rclone-honestly · /docs/connect/cloudflare-r2 · /docs/connect/aws-s3 · /pricing

External references

Cloudflare R2 docs (Super Slurper, Sippy, S3 compatibility) · AWS S3 pricing and Glacier restore documentation · rclone documentation

Suggested featured image

The migration render from the northwind set: source bucket to destination bucket with the byte counter running.

Supporting visuals

The six-gotcha list as a LinkedIn carousel. The multipart ETag explanation as a single diagram.

⚠️ Pre-publish verification

Confirm R2's Object Lock status at publication time; if it has shipped since writing, correct the claim. Re-verify all pricing. Re-verify Super Slurper terms.

Your storage estate deserves a control plane.

Join the DevOps teams and founders who run every cloud's buckets from one control plane.

Free plan  ·  No credit card  ·  50+ cloud providers  ·  Cancel any time