$linuxjunkies
>

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.

IntermediateUbuntuDebianFedoraArch11 min readUpdated June 7, 2026

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_REPOSITORY variable and run restic snapshots to 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 with rclone ls b2-offsite:your-bucket --b2-versions to see hidden objects.
  • Slow uploads on metered connections — Limit bandwidth with rclone --bwlimit 5M or restic's --limit-upload 5000 (in KiB/s).
  • Borg lock errors after interrupted backup — Run borg break-lock /path/to/repo only when you are certain no other borg process is running.
tested on:Ubuntu 24.04Debian 12Fedora 40Arch rolling

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