How to copy an S3 bucket to another provider with rclone
Set up an rclone remote for each provider, run rclone copy source:bucket destination:bucket --checksum --fast-list --dry-run, then run it again without --dry-run and confirm with rclone check --one-way. Between providers every byte passes through the machine running rclone, so the source provider bills its egress: run the copy close to the source and price it first. Use copy, not sync, for a backup copy, because sync deletes from the destination whatever has gone from the source.
Where the data goes
Within one remote, rclone asks the provider to copy each object itself (a server-side copy), and no data passes through your machine:
rclone copy aws:acme-backups aws:acme-backups-archiverclone's S3 docs say both sides must use the same remote. --server-side-across-configs tries it between two differently configured remotes of the same backend; it is off by default.
Between providers there is no server-side copy. In the rclone FAQ's words, it "effectively downloads the file and uploads it again, so the node running rclone would need to have lots of bandwidth." Pick that machine so one leg is free:
- From AWS S3: an EC2 instance in the bucket's region, which reads S3 for free. AWS still bills the data leaving AWS, from EC2 instead of S3, but no second provider's bandwidth is used.
- From DigitalOcean Spaces: a Droplet in a datacenter DigitalOcean lists as free for that bucket's region (NYC3 to NYC1, NYC2 and NYC3, for example). The upload then uses the Droplet's own transfer allowance.
- From anywhere else: a server near the source's region with enough outbound transfer for the whole copy.
Set up both remotes
The rclone backup guide covers installing a current rclone and creating remotes. A copy from AWS to R2 needs two:
[aws]
type = s3
provider = AWS
access_key_id = YOUR_AWS_KEY_ID
secret_access_key = YOUR_AWS_SECRET
region = us-east-1
[r2]
type = s3
provider = Cloudflare
access_key_id = YOUR_R2_KEY_ID
secret_access_key = YOUR_R2_SECRET
region = auto
endpoint = https://ACCOUNT_ID.r2.cloudflarestorage.com
acl = private
no_check_bucket = trueno_check_bucket = true stops rclone checking for or creating the bucket, which R2 tokens with Object Read & Write permission may need. The file holds both keys: chmod 600 it. Settings for other providers:
| Storage | type | provider | region | endpoint |
|---|---|---|---|---|
| AWS S3 | s3 | AWS | The bucket's region | Leave empty |
| DigitalOcean Spaces | s3 | DigitalOcean | Leave empty | <region>.digitaloceanspaces.com |
| Cloudflare R2 | s3 | Cloudflare | auto | https://<account-id>.r2.cloudflarestorage.com |
| Wasabi | s3 | Wasabi | Leave empty | s3.wasabisys.com (us-east-1) or your region's endpoint |
| Backblaze B2, native | b2 | None | None | Leave empty; account is the application key ID, key the application key |
| Backblaze B2, S3 API | s3 | Other | The bucket's region | https://s3.<region>.backblazeb2.com |
| Google Drive | drive | None | None | OAuth sign-in; see the Google Drive guide |
rclone lists no Backblaze S3 provider, so B2's S3 endpoint uses Other. For Drive, rclone's docs report an undocumented 750 GiB daily upload limit and about 2 files per second; --drive-stop-on-upload-limit stops the run at the limit instead of retrying.
Measure the bucket and price the egress
rclone size aws:acme-backupsIt prints the object count and total size: what will leave the source. Published terms, as of October 2026:
| Source | What data leaving it costs |
|---|---|
| AWS S3 | First 100 GB a month to the internet free, shared across all AWS services; then per GB, tiered by volume and region. Free to buckets and services in the same region. |
| DigitalOcean Spaces | 1,024 GiB a month included per subscription, shared by all buckets, then $0.01 per GiB. Free to Droplets in the listed datacenters. |
| Cloudflare R2 | No egress charges. Each read counts as a Class B operation (GetObject, HeadObject). |
| Wasabi | No egress fees while monthly egress stays at or below your active storage; regularly exceeding it can get the service limited or suspended. |
| Backblaze B2 | Free up to 3 times your average monthly storage, then $0.01 per GB. Free through partner CDNs and compute providers. |
| Google Drive | Quotas rather than fees: rclone reports an undocumented 10 TiB download limit a day. |
Example: copying 800 GB from S3 to R2. After the free 100 GB, AWS bills 700 GB at its internet rate; at the $0.09 per GB that the S3 pricing page uses in its Europe (Ireland) example, that is $63. The same 800 GB out of Wasabi costs nothing if you store at least 800 GB there, and out of B2 nothing if your average storage that month was at least 267 GB.
Leaving AWS for good? AWS offers eligible customers free data transfer out when they move all their data off AWS or off one service. Ask AWS Support first.
copy, not sync
rclone copy adds new and changed objects and never deletes. rclone sync makes the destination identical, deleting whatever the source no longer has, so if the source is emptied by a mistake, a lifecycle rule or an attacker, the next sync empties the copy. Use copy, and give the destination its own lifecycle rule.
If you need a mirror, destination versioning softens deletes: on a versioned S3 bucket, and on B2 by default, a delete hides the current version rather than destroying it. Add --max-delete as a brake. Look before the first run:
rclone copy aws:acme-backups r2:acme-backups --checksum --fast-list --dry-runRun the copy
rclone copy aws:acme-backups r2:acme-backups --checksum --fast-list --transfers 16 --checkers 32 -P --log-file /var/log/bucket-copy.log --log-level INFO--checksumcompares size and MD5 from the listings. rclone's S3 docs recommend it when both sides are S3: it needs no extra requests, while the default modification-time check costs a HEAD request per object.--fast-listlists 1,000 objects per request instead of directory by directory, at about 1 KB of memory per object: a million objects need roughly 1 GiB of RAM. Providers bill listings; on R2,ListObjectsis a Class A operation.--transfers 16copies 16 objects at once (default 4);--checkers 32compares 32 (default 8). Objects over 200 MiB upload in parts, buffering 4 chunks of 5 MiB per transfer by default, so 16 transfers can hold 320 MiB. For B2, rclone's docs found about 32 transfers best.-Pshows progress;--log-filekeeps a record.
How rclone decides an object is already there
By default, rclone treats objects with the same size and modification time as identical. With --checksum it compares size and hash, which only works when both sides store the same hash type: MD5 for S3, SHA-1 for B2's native backend, and MD5, SHA-1 and SHA-256 for Drive.
- S3 to S3, or S3 to Drive: MD5 on both sides, when the object has one. An S3 ETag is the MD5 only for single-part uploads; for multipart uploads, rclone's docs say it "can't easily be checked against the file as the chunk size must be known in order to calculate it". rclone saves the real MD5 as
X-Amz-Meta-Md5chksummetadata on its own multipart uploads, but objects other tools uploaded in parts have no readable MD5 and compare by size only. - S3 to B2's native backend: no hash in common. rclone logs
--checksum is in use but the source and destination have no hashes in common; falling back to --size-only. Drop--checksumso it compares modification times, or reach B2 through its S3 endpoint and confirm withrclone md5sumthat objects there report an MD5.
To see which objects carry an MD5, list them. Objects without one print with a blank hash:
rclone md5sum aws:acme-backups/web-01Verify the copy
rclone check aws:acme-backups r2:acme-backups --one-way --fast-listrclone check compares sizes and hashes and changes nothing; --one-way only checks that every source object arrived. Read the summary lines: differences found (non-zero also means a non-zero exit code), hashes could not be checked (compared by size alone, as above) and matching files. Single objects show sizes differ or md5 differ.
For objects without usable hashes, --download compares the bytes in memory, but it downloads from both providers, paying egress twice: run it on a sample, such as one server's folder. Matching bytes still don't prove a backup restores, so test a restore from the copy.
Versioned buckets and Object Lock
- Only current versions copy. Older versions and delete markers stay behind.
--s3-versionsshows old versions as extra files with a timestamp in the name, so copying with it creates separate objects, not versions. - A past state can copy.
version_atshows a versioned bucket as it was at a date. Set it on the source only, with a connection string, since rclone allows no writes through it. B2's native backend has it too.
rclone copy "aws,version_at=2026-10-01:acme-backups" r2:acme-backups-2026-10-01 --dry-run- Object Lock retention doesn't copy by default. rclone 1.74 and newer can set it when uploading to a bucket with S3 Object Lock:
--s3-object-lock-modeand--s3-object-lock-retain-until-date, which only work together. With--metadata, the valuecopytakes both from each source object, as below. - A destination's default retention applies anyway, dated from when each object lands, so a copied backup stays locked for the full period from the copy, not from its original date.
rclone copy aws:acme-backups wasabi:acme-locked --metadata --s3-object-lock-mode copy --s3-object-lock-retain-until-date copyKeep a second copy on a schedule
A copy at another provider, in another account, is the off-site copy of the 3-2-1 rule. This script copies new backups every night, then checks them:
#!/usr/bin/env bash
set -euo pipefail
CONF=/root/.config/rclone/rclone.conf
LOG=/var/log/bucket-copy.log
rclone copy aws:acme-backups r2:acme-backups --config "$CONF" \
--checksum --fast-list --transfers 16 --log-file "$LOG" --log-level INFO
rclone check aws:acme-backups r2:acme-backups --config "$CONF" \
--one-way --fast-list --log-file "$LOG" --log-level INFO15 4 * * * root flock -n /run/bucket-copy.lock /usr/local/bin/bucket-copy.sh- Make it executable with
chmod 700and schedule it after the backups land;flock -nskips a run while the last is still going. See scheduling backups with cron. - Give rclone a read-only source key and, where the provider allows it, a destination key that cannot delete.
copynever needs to, and a compromised copy machine then can't erase both copies. - Expire old backups at the destination with a lifecycle rule that matches your retention policy.
- Both commands exit non-zero on failure; make sure someone hears about it, as in backup failure alerts.
Common errors
| Error or symptom | Fix |
|---|---|
--checksum is in use but the source and destination have no hashes in common; falling back to --size-only | The backends share no hash type, as with S3 and B2's native backend. Remove --checksum, or use B2's S3 endpoint if rclone md5sum shows MD5s there. |
hashes could not be checked in rclone check | Those objects have no MD5, usually multipart uploads by other tools. Their sizes matched; check a sample with --download. |
Object in GLACIER, restore first | The source object is in an archive storage class. Restore it in S3 first; retrieval is billed. |
incorrect region, the bucket is not in 'XXX' region | Set the AWS remote's region to the bucket's region. |
| Uploads to R2 fail with an object-only token | Add no_check_bucket = true to the R2 remote. |
| 401 errors from B2 | Use the application key's ID as account, not the master account ID. |
Frequently asked questions
- Can rclone copy directly between two cloud providers?
- Yes, but not server-side: each object is downloaded and uploaded again by the machine running rclone, so you pay the source's egress.
- Does rclone sync delete files on the destination?
- Yes, anything no longer in the source. rclone copy never deletes, which is safer for backup copies.
- Does rclone copy object versions?
- No, only the current version of each object. To copy a bucket as it was at a date, read the source with version_at.
- How much does it cost to copy a bucket to another provider?
- Mostly the source's egress. As of October 2026, AWS bills per GB after the first 100 GB a month, R2 charges no egress, and Wasabi and B2 include egress up to limits tied to how much you store.
How this was checked
Commands, limits and prices were checked against these official pages, on October 4, 2026:
- rclone: Amazon S3 Storage Providers
- rclone: Backblaze B2
- rclone: Google Drive
- rclone: Overview of cloud storage systems (hashes, features)
- rclone: Documentation (global flags, connection strings)
- rclone: FAQ
- rclone copy
- rclone sync
- rclone check
- rclone md5sum
- rclone size
- rclone: Changelog
- rclone source (check and --checksum log messages)
- Amazon S3 pricing
- Amazon EC2 On-Demand pricing (data transfer)
- Amazon S3 User Guide: Locking objects with Object Lock
- DigitalOcean Spaces pricing
- Cloudflare R2 pricing
- Wasabi Pay as You Go pricing FAQ
- Backblaze B2 pricing