VPS Snaps

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.

10 min readUpdated Checked against official documentation

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:

Terminal
rclone copy aws:acme-backups aws:acme-backups-archive

rclone'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:

/root/.config/rclone/rclone.conf
[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 = true

no_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:

Storagetypeproviderregionendpoint
AWS S3s3AWSThe bucket's regionLeave empty
DigitalOcean Spacess3DigitalOceanLeave empty<region>.digitaloceanspaces.com
Cloudflare R2s3Cloudflareautohttps://<account-id>.r2.cloudflarestorage.com
Wasabis3WasabiLeave emptys3.wasabisys.com (us-east-1) or your region's endpoint
Backblaze B2, nativeb2NoneNoneLeave empty; account is the application key ID, key the application key
Backblaze B2, S3 APIs3OtherThe bucket's regionhttps://s3.<region>.backblazeb2.com
Google DrivedriveNoneNoneOAuth 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

Terminal
rclone size aws:acme-backups

It prints the object count and total size: what will leave the source. Published terms, as of October 2026:

SourceWhat data leaving it costs
AWS S3First 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 Spaces1,024 GiB a month included per subscription, shared by all buckets, then $0.01 per GiB. Free to Droplets in the listed datacenters.
Cloudflare R2No egress charges. Each read counts as a Class B operation (GetObject, HeadObject).
WasabiNo egress fees while monthly egress stays at or below your active storage; regularly exceeding it can get the service limited or suspended.
Backblaze B2Free up to 3 times your average monthly storage, then $0.01 per GB. Free through partner CDNs and compute providers.
Google DriveQuotas 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:

Terminal
rclone copy aws:acme-backups r2:acme-backups --checksum --fast-list --dry-run

Run the copy

Terminal
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
  • --checksum compares 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-list lists 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, ListObjects is a Class A operation.
  • --transfers 16 copies 16 objects at once (default 4); --checkers 32 compares 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.
  • -P shows progress; --log-file keeps 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-Md5chksum metadata 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 --checksum so it compares modification times, or reach B2 through its S3 endpoint and confirm with rclone md5sum that objects there report an MD5.

To see which objects carry an MD5, list them. Objects without one print with a blank hash:

Terminal
rclone md5sum aws:acme-backups/web-01

Verify the copy

Terminal
rclone check aws:acme-backups r2:acme-backups --one-way --fast-list

rclone 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-versions shows 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_at shows 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.
Terminal
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-mode and --s3-object-lock-retain-until-date, which only work together. With --metadata, the value copy takes 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.
Terminal
rclone copy aws:acme-backups wasabi:acme-locked --metadata --s3-object-lock-mode copy --s3-object-lock-retain-until-date copy

Keep 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/local/bin/bucket-copy.sh
#!/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 INFO
/etc/cron.d/bucket-copy
15 4 * * * root flock -n /run/bucket-copy.lock /usr/local/bin/bucket-copy.sh
  • Make it executable with chmod 700 and schedule it after the backups land; flock -n skips 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. copy never 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 symptomFix
--checksum is in use but the source and destination have no hashes in common; falling back to --size-onlyThe 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 checkThose objects have no MD5, usually multipart uploads by other tools. Their sizes matched; check a sample with --download.
Object in GLACIER, restore firstThe source object is in an archive storage class. Restore it in S3 first; retrieval is billed.
incorrect region, the bucket is not in 'XXX' regionSet the AWS remote's region to the bucket's region.
Uploads to R2 fail with an object-only tokenAdd no_check_bucket = true to the R2 remote.
401 errors from B2Use 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: