Offsite Backups to Backblaze B2 / R2 / S3
Back up Linux servers to Backblaze B2, Cloudflare R2, or AWS S3 using rclone, restic, and BorgBackup—with lifecycle policies and restore testing.
Before you start
- ▸An active account with Backblaze B2, Cloudflare R2, or AWS S3 with a bucket created and API keys generated
- ▸Root or sudo access on the Linux machine being backed up
- ▸Sufficient local disk space for a Borg local repo if using the Borg + rclone pattern
Offsite backups are your last line of defence. A backup that lives on the same machine—or even the same building—as your data isn't an offsite backup. This guide walks through three popular tools (rclone, restic, and BorgBackup) against S3-compatible object storage (Backblaze B2, Cloudflare R2, and AWS S3), covers bucket lifecycle policies to control costs, and ends with how to verify your backups actually restore correctly.
Choosing a Storage Backend
All three services expose an S3-compatible API, which means the same tools and concepts apply across them.
- Backblaze B2 — cheapest at-rest storage; free egress to Cloudflare partners; good free tier.
- Cloudflare R2 — zero egress fees; priced per operation; no free restore bandwidth regardless of destination.
- AWS S3 — most mature; most integrations; watch egress and request costs on large restores.
For home servers and small businesses, B2 is usually the most cost-effective. For workloads that restore frequently, R2's zero-egress model wins.
Install the Tools
rclone
rclone ships in most distro repos but the upstream binary is always newer. Use the official installer for the latest version:
curl https://rclone.org/install.sh | sudo bash
restic
# Debian / Ubuntu
sudo apt install restic
# Fedora / RHEL 9+ / Rocky
sudo dnf install restic
# Arch
sudo pacman -S restic
After installing, run restic self-update (as root) to pull the latest binary regardless of distro packaging lag.
BorgBackup
# Debian / Ubuntu
sudo apt install borgbackup
# Fedora / RHEL (EPEL required on RHEL/Rocky)
sudo dnf install borgbackup
# Arch
sudo pacman -S borg
Borg doesn't speak S3 natively. You pair it with borgmatic and an rclone or SFTP target, or use the borg-s3 wrapper. The cleanest approach for S3 backends is to run borg locally and use rclone to sync the finished repo. That pattern is covered below.
Configure rclone Remotes
rclone uses named remotes stored in ~/.config/rclone/rclone.conf. Run the interactive wizard or write the config directly.
Backblaze B2
rclone config create b2-offsite b2 \
account YOUR_B2_KEY_ID \
key YOUR_B2_APP_KEY
Cloudflare R2
rclone config create r2-offsite s3 \
provider Cloudflare \
access_key_id YOUR_R2_KEY_ID \
secret_access_key YOUR_R2_SECRET \
endpoint https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com \
acl private
AWS S3
rclone config create s3-offsite s3 \
provider AWS \
access_key_id YOUR_KEY_ID \
secret_access_key YOUR_SECRET \
region us-east-1 \
acl private
Test any remote immediately:
rclone lsd b2-offsite:
Offsite Backups with restic
restic handles deduplication, encryption, and pruning in one tool. It speaks S3 natively via its s3 backend (which covers B2, R2, and AWS S3).
Initialize a restic repository on B2
export RESTIC_REPOSITORY="s3:https://s3.us-west-004.backblazeb2.com/your-bucket-name"
export RESTIC_PASSWORD="a-strong-passphrase"
export AWS_ACCESS_KEY_ID="your-b2-key-id"
export AWS_SECRET_ACCESS_KEY="your-b2-app-key"
restic init
For R2, change the endpoint to s3:https://YOUR_ACCOUNT.r2.cloudflarestorage.com/your-bucket. For AWS S3, use s3:s3.amazonaws.com/your-bucket.
Run a backup
restic backup /etc /home /var/www \
--exclude-caches \
--exclude '/home/*/.cache' \
--tag daily
Prune old snapshots
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune
Automate with a systemd timer
sudo tee /etc/systemd/system/restic-offsite.service <<'EOF'
[Unit]
Description=restic offsite backup
[Service]
Type=oneshot
EnvironmentFile=/etc/restic/b2.env
ExecStart=/usr/local/bin/restic backup /etc /home /var/www --exclude-caches --tag daily
ExecStartPost=/usr/local/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF
sudo tee /etc/systemd/system/restic-offsite.timer <<'EOF'
[Unit]
Description=Run restic offsite backup daily
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now restic-offsite.timer
Store credentials in /etc/restic/b2.env (mode 0600, owned root) so they never appear in process listings.
Offsite Backups with BorgBackup + rclone
Borg produces excellent deduplicated, encrypted local repos. Pair it with rclone to push the finished repo offsite.
# Create a local borg repo
borg init --encryption=repokey /mnt/backup/borg-repo
# Run a backup
borg create --stats --compression lz4 \
/mnt/backup/borg-repo::'{hostname}-{now}' \
/etc /home /var/www
# Prune
borg prune /mnt/backup/borg-repo \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6
# Sync repo to B2
rclone sync /mnt/backup/borg-repo b2-offsite:your-bucket/borg-repo \
--transfers 4 --b2-hard-delete
The --b2-hard-delete flag bypasses B2's hide-then-expire mechanism and removes objects immediately, preventing surprise storage bills from hidden versions. On R2 or S3 use --s3-no-check-bucket if the bucket already exists and you hit permission errors.
Lifecycle Policies
Without lifecycle rules, deleted or overwritten objects accumulate and cost money—especially on B2 which retains hidden file versions by default.
Backblaze B2
In the B2 console, go to Buckets → Lifecycle Rules. Set Keep only the last version and expire file versions after 1 day. This removes hidden versions quickly. Alternatively, pass --b2-hard-delete in rclone as shown above.
AWS S3
Apply a lifecycle policy via the AWS CLI:
aws s3api put-bucket-lifecycle-configuration \
--bucket your-bucket \
--lifecycle-configuration '{
"Rules": [{
"ID": "expire-old-versions",
"Status": "Enabled",
"NoncurrentVersionExpiration": {"NoncurrentDays": 30},
"AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
}]
}'
Cloudflare R2
R2 supports lifecycle rules via the S3 API (as above) or through the Cloudflare dashboard under R2 → your bucket → Settings → Lifecycle rules. Add a rule to delete incomplete multipart uploads after 7 days—R2 doesn't charge for storage but you pay per operation on lingering parts.
Restore Testing
A backup you haven't restored from is a backup you don't trust. Schedule restore tests quarterly at minimum.
restic restore test
# List snapshots
restic snapshots
# Check repository integrity
restic check --read-data-subset=10%
# Restore a specific path to a temp directory
restic restore latest --target /tmp/restore-test --include /etc/ssh
Verify that restored config files match known-good checksums or that services start correctly against them.
Borg restore test
# Pull repo back from B2 first (if testing full remote restore)
rclone sync b2-offsite:your-bucket/borg-repo /tmp/borg-restore-test
# Mount and inspect
mkdir /tmp/borg-mount
borg mount /tmp/borg-restore-test /tmp/borg-mount
ls /tmp/borg-mount
# Unmount when done
borg umount /tmp/borg-mount
Troubleshooting
- restic: "Fatal: unable to open config file" — The repository hasn't been initialised or the endpoint URL is wrong. Double-check the
RESTIC_REPOSITORYvariable and runrestic snapshotsto confirm connectivity. - rclone: 403 Forbidden on B2 — Your application key may not have Read and Write permissions for the bucket. Generate a new key scoped to the specific bucket in the B2 console.
- B2 hidden versions accumulating — Enable lifecycle rules or always pass
--b2-hard-delete. Check withrclone ls b2-offsite:your-bucket --b2-versionsto see hidden objects. - Slow uploads on metered connections — Limit bandwidth with
rclone --bwlimit 5Mor restic's--limit-upload 5000(in KiB/s). - Borg lock errors after interrupted backup — Run
borg break-lock /path/to/repoonly when you are certain no other borg process is running.
Frequently asked questions
- Can I use the same rclone remote for both rclone sync and restic?
- No—restic manages its own repository structure directly via the S3 API. Use a separate bucket or key-prefix for restic repos and a different one for bare rclone syncs to avoid conflicts.
- How do I encrypt data before it leaves my server?
- Both restic and Borg encrypt at rest and in transit by default using your passphrase and AES-256/ChaCha20. rclone alone does not encrypt; add an rclone 'crypt' remote layered on top of the storage remote if you need encryption with rclone sync.
- Will versioning on B2 or S3 protect against ransomware?
- Bucket versioning helps but isn't sufficient alone—ransomware that compromises your API keys can delete versions too. Use immutable object lock (Object Lock on S3, Object Lock on B2) with a WORM policy for genuine ransomware protection.
- How much does it cost to back up 500 GB to Backblaze B2?
- At B2's current pricing (~$0.006/GB/month), 500 GB costs roughly $3/month in storage. Uploads are free; downloads are $0.01/GB after the first 1 GB/day free tier. Egress to Cloudflare-partnered services (including many CDNs) is free.
- Can I back up a running database with these tools?
- You should never back up live database files directly—they will be inconsistent. Dump the database first (pg_dump, mysqldump) to a file, then include that dump file in your restic or borg backup run.
Related guides
Back Up Linux with Borg or restic
Set up encrypted, deduplicated backups with BorgBackup or restic: local and remote repos, retention pruning, restoring files, and systemd timer scheduling.
How to Check Disk Health with SMART
Learn to use smartctl to read SMART attributes, run drive self-tests, and identify early warning signs of HDD and SSD failure before data loss occurs.
Debug systemd Units that Won't Start
Learn a repeatable workflow to debug systemd services that won't start: status output, journalctl, systemd-analyze verify, and safe override.conf patches.
Linux Server Disaster Recovery Checklist
A practical Linux server disaster recovery checklist: what to back up, RTO/RPO planning, immutable off-site copies, automated restore drills, and verification.