Automated Off-Site Backups with restic

By LK Wood IV · 2026-06-13 · ~13 min read · St. Louis County, MO

A backup that doesn’t run automatically doesn’t run. A backup that isn’t off-site doesn’t protect you from physical disasters. A backup you haven’t tested restoring from might not actually work. This guide sets up restic — the best homelab backup tool available — with Backblaze B2 as the off-site destination, automated scheduling via systemd, and monitoring so you know when backups fail.

Why restic

restic is a fast, encrypted, deduplicated backup tool written in Go. It:

  • Encrypts everything client-side before upload — Backblaze can’t read your data
  • Deduplicates at the chunk level — backing up files that haven’t changed costs nothing after the first backup
  • Handles incremental backups natively — no need to manage “full” vs “differential” schedules
  • Supports many backends — B2, S3, SFTP, Wasabi, local filesystem, and more
  • Has no backup agent required on the destination — just the restic binary on your machine and credentials to the remote

Step 1: Install restic

# Debian/Ubuntu
apt install -y restic

# Or download the latest binary directly
wget https://github.com/restic/restic/releases/latest/download/restic_linux_amd64.bz2
bzip2 -d restic_linux_amd64.bz2
mv restic_linux_amd64 /usr/local/bin/restic
chmod +x /usr/local/bin/restic

# Verify
restic version

Step 2: Set up Backblaze B2

  1. Create a Backblaze account at backblaze.com
  2. Create a bucket: Buckets → Create a Bucket
    • Bucket name: homelab-restic-backups-yourname (must be globally unique)
    • Files in Bucket: Private
    • Default Encryption: Enabled (optional — restic encrypts anyway, but defense in depth)
    • Object Lock: Optional — enables immutable storage, protects against ransomware deleting backups
  3. Create an Application Key: App Keys → Add a New Application Key
    • Name: restic-homelab
    • Allow access to: only the bucket you created
    • Type of Access: Read and Write
    • Note the keyID and applicationKey — they’re shown once

Step 3: Create the environment file

Store your credentials in a protected file — not in scripts or cron commands:

mkdir -p /etc/restic
chmod 700 /etc/restic

cat > /etc/restic/b2.env << 'EOF'
B2_ACCOUNT_ID=your-keyID-here
B2_ACCOUNT_KEY=your-applicationKey-here
RESTIC_REPOSITORY=b2:homelab-restic-backups-yourname:/
RESTIC_PASSWORD=your-strong-backup-password-here
EOF

chmod 600 /etc/restic/b2.env

The repository password (RESTIC_PASSWORD) encrypts all your backup data. If you lose it, your backup data is permanently inaccessible. Store this password:

  • In your password manager (Vaultwarden/Bitwarden)
  • In a physical printout stored somewhere safe
  • NOT only on the machine you’re backing up

Step 4: Initialize the repository

source /etc/restic/b2.env
restic init

You should see:

created restic repository abc123def at b2:homelab-restic-backups-yourname:/

This creates the repository structure in your B2 bucket. It stores encrypted keys and an index in the bucket root.

Step 5: First backup

Test with a manual backup of a small directory first:

source /etc/restic/b2.env

# Back up a single directory
restic backup /opt/stacks/vaultwarden/data

# Check what was backed up
restic snapshots

You should see a snapshot entry with the date, hostname, and paths backed up.

For a homelab, back up:

  • Docker service data directories: /opt/stacks/*/data
  • Configuration files: /etc/
  • Home directories: /home/
  • Anything in /opt/ you care about
restic backup \
  /opt/stacks \
  /etc \
  /home \
  --exclude "*.tmp" \
  --exclude "**/cache/**" \
  --exclude "**/node_modules/**"

The --exclude flags keep temporary files out of the backup. Add more excludes for large directories you don’t need to back up (video files, ISO images).

Step 6: Automate with systemd

Create two files: a service (what to run) and a timer (when to run it).

# /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic backup to Backblaze B2
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/b2.env
ExecStart=/usr/bin/restic backup \
  /opt/stacks \
  /etc \
  /home \
  --exclude "*.tmp" \
  --exclude "**/cache/**" \
  --exclude "**/node_modules/**" \
  --verbose
ExecStartPost=/usr/bin/restic forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --prune
StandardOutput=journal
StandardError=journal
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Run Restic backup daily at 3am

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=900

[Install]
WantedBy=timers.target

The RandomizedDelaySec=900 adds a random 0–15 minute delay to the scheduled time. This prevents all your nodes from hammering B2 at exactly 3:00:00am simultaneously.

Enable and start:

systemctl daemon-reload
systemctl enable --now restic-backup.timer

# Verify timer is active
systemctl list-timers restic-backup.timer

Run it manually to confirm it works:

systemctl start restic-backup.service
journalctl -u restic-backup.service -f

Step 7: Retention policy

The restic forget --prune command in the service removes old snapshots based on your retention policy. The settings above keep:

--keep-daily 7    → one snapshot per day for the last 7 days
--keep-weekly 4   → one per week for the last 4 weeks
--keep-monthly 6  → one per month for the last 6 months

This gives you granular recovery for recent data and longer-term recovery points. After 6 months, only one snapshot per month is kept.

Total snapshots over 6 months: at most 7 + 4 + 6 = 17 snapshots. Because of deduplication, the actual storage cost depends on how much data changed between backups — unchanged files across all 17 snapshots are stored only once.

Adjust the retention to match your needs and your B2 storage budget.

Step 8: Monitor — know when backups fail

Silent backup failures are more dangerous than no backup at all — they create false confidence.

Option A: Uptime Kuma push monitor. Add this to the end of your systemd service to ping Uptime Kuma on success:

ExecStartPost=/usr/bin/curl -fsS \
  "https://uptime.yourdomain.com/api/push/YOUR-PUSH-KEY?status=up&msg=restic+OK"

If the service fails before reaching ExecStartPost, or if the curl itself fails, Uptime Kuma doesn’t receive the push and alerts you.

Option B: healthchecks.io (free tier). healthchecks.io is a service designed specifically for this: you ping a URL after each successful backup, and it alerts you if the ping stops coming. The free tier covers 20 checks.

# Add to ExecStartPost:
/usr/bin/curl -fsS https://hc-ping.com/YOUR-UUID

Check backup status manually:

source /etc/restic/b2.env
restic snapshots    # list all snapshots
restic check        # verify repository integrity

Run restic check weekly. It downloads metadata and verifies chunk hashes — it will catch a corrupt repository before you need a restore.

Step 9: Test a restore

Do this before you need it:

source /etc/restic/b2.env

# List snapshots
restic snapshots

# Restore a specific file to a temp directory
restic restore latest \
  --target /tmp/restic-test-restore \
  --include /opt/stacks/vaultwarden/data/db.sqlite3

# Verify the file is there and readable
ls -la /tmp/restic-test-restore/opt/stacks/vaultwarden/data/
sqlite3 /tmp/restic-test-restore/opt/stacks/vaultwarden/data/db.sqlite3 ".tables"

Do a full directory restore to verify your most important data at least monthly.

Advanced: backing up Proxmox PBS datastores

If you’re running Proxmox Backup Server for VM/LXC backups, you can use restic to push PBS datastore contents to B2 as the off-site layer:

# /etc/systemd/system/restic-pbs.service
[Unit]
Description=Restic backup of PBS datastore to B2
After=pve-storage-local.mount

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/b2-pbs.env
ExecStart=/usr/bin/restic backup /mnt/backup-drive/pve-backups \
  --tag pbs-datastore
ExecStartPost=/usr/bin/restic forget \
  --tag pbs-datastore \
  --keep-daily 3 --keep-weekly 2

This gives you a 3-2-1 stack:

  • Copy 1: live Proxmox VMs on the production host
  • Copy 2: PBS local backup
  • Copy 3: restic → B2 (off-site, encrypted)

Bandwidth and cost estimate

For a homelab with 200GB of unique backup data, after the initial upload:

  • Initial B2 upload: 200GB × $0 (B2 doesn’t charge upload bandwidth) = $0
  • Monthly storage: 200GB × $0.006 = $1.20/month
  • Monthly incremental upload: if 5GB changes per day × 30 days = 150GB new data, but after deduplication maybe 3GB/month net new = $0.018/month extra
  • Total B2 cost: ~$1.20–1.50/month

For 100GB of backup data: ~$0.60/month. Off-site encrypted backup for less than a cup of coffee.


The Proxmox Backup Server guide covers the PBS layer (copy 2 in the 3-2-1 stack). This guide covers copy 3. Together they complete a full 3-2-1 backup strategy.