Skip to main content

Automated Server Backups with Restic and Rclone 2026

·OSSAlt Team
resticrclonebackupself-hostingdevopsautomation2026

TL;DR

Restic + Rclone is the gold standard for self-hosted server backups in 2026. Restic handles encrypted, deduplicated snapshots — perfect for VPS data, Docker volumes, and config files. Rclone syncs the backup repository to any cloud storage (Backblaze B2, S3, Wasabi, SFTP). Together: automated encrypted offsite backups for ~$3/month in storage costs. Never lose data again.

Key Takeaways

  • Restic: BSD 2-Clause, ~26K stars, Go — encrypted, deduplicated, compressed backups
  • Rclone: MIT, ~46K stars, Go — syncs to 70+ cloud storage providers
  • Deduplication: Restic only stores changed chunks — a 10GB backup with 1GB changed uses ~1GB of space
  • Encryption: AES-256, ChaCha20-Poly1305 — all data encrypted before leaving your server
  • 3-2-1 rule: 3 copies, 2 different media, 1 offsite — Restic + B2 achieves this easily
  • Cost: Backblaze B2 at $0.006/GB/month — 100GB = $0.60/month

Why Restic + Rclone?

ToolRoleAlternative
ResticCreate snapshots (dedup, encrypt, verify)Borg, Duplicati, BorgBase
RcloneSync repo to cloud storageaws s3 sync, rclone

Restic vs Borg: Both are excellent. Restic is simpler to set up, supports more backends natively, and has better cross-platform support. Borg is faster and has a larger community in the Linux server space. Either works.

Restic directly supports cloud backends (S3, B2, SFTP, etc.) without Rclone — but Rclone gives you 70+ providers with a single config format. The combination gives maximum flexibility.


Part 1: Install Restic and Rclone

# Ubuntu/Debian:
apt-get install -y restic

# macOS:
brew install restic

# Direct download (any Linux):
wget https://github.com/restic/restic/releases/latest/download/restic_linux_amd64.bz2
bunzip2 restic_linux_amd64.bz2
chmod +x restic_linux_amd64
mv restic_linux_amd64 /usr/local/bin/restic

# Install Rclone:
curl https://rclone.org/install.sh | bash

Backblaze B2 is the cheapest major cloud storage at $0.006/GB/month ($6/TB). 10x cheaper than AWS S3.

  1. Create a Backblaze account
  2. Create a bucket: my-server-backups
  3. Create an Application Key with read/write access to that bucket

Configure Rclone for B2

rclone config

Follow the wizard:

  • n → New remote
  • Name: b2
  • Storage: Backblaze B2
  • Account: your B2 Account ID
  • Key: your Application Key
  • Leave other settings as default

Verify:

rclone ls b2:my-server-backups
# Should list empty (or existing files)

Part 3: Initialize Restic Repository

# Set password as env var (or use a password file):
export RESTIC_PASSWORD="your-strong-backup-password-here"
export RESTIC_REPOSITORY="rclone:b2:my-server-backups"

# Initialize repository:
restic init

# Output:
# created restic repository abc12345 at rclone:b2:my-server-backups
# Please note that knowledge of your password is required to access
# the repository. Losing your password means that your data is
# irrecoverably lost!

Save your password somewhere safe — without it, your backups are permanently inaccessible (that's the encryption working as intended).

For local backup repository (fast, for dev):

export RESTIC_REPOSITORY="/backup/local-restic"
restic init

Part 4: Your First Backup

# Backup a directory:
restic backup /opt/docker /etc /home

# With tags (label for filtering snapshots later):
restic backup /opt/docker --tag docker,production

# Backup a specific Docker volume mount path:
restic backup /var/lib/docker/volumes/nextcloud_data/_data \
  --tag nextcloud

# Dry run (see what would be backed up):
restic backup /opt/docker --dry-run -v

Exclude Patterns

restic backup /home \
  --exclude="*/node_modules" \
  --exclude="*/.git" \
  --exclude="*/vendor" \
  --exclude="*.log" \
  --exclude="*/cache/*"

Part 5: Automate with a Backup Script

Create /usr/local/bin/backup.sh:

#!/bin/bash
# /usr/local/bin/backup.sh — Automated Restic backup
set -euo pipefail

# Configuration
export RESTIC_REPOSITORY="rclone:b2:my-server-backups"
export RESTIC_PASSWORD_FILE="/root/.restic-password"   # More secure than env var
export RCLONE_CONFIG="/root/.config/rclone/rclone.conf"

# Logging
LOG_FILE="/var/log/backup.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

log() {
  echo "[$TIMESTAMP] $1" | tee -a "$LOG_FILE"
}

# --- Backup Docker volumes and configs ---
log "Starting backup..."

restic backup \
  /opt/docker \
  /etc \
  /root/.config \
  --tag server,$(hostname) \
  --exclude="*/node_modules" \
  --exclude="*/.git" \
  --exclude="*.tmp" \
  >> "$LOG_FILE" 2>&1

log "Backup completed."

# --- Forget old snapshots (retention policy) ---
restic forget \
  --keep-last 7 \
  --keep-daily 14 \
  --keep-weekly 8 \
  --keep-monthly 6 \
  --prune \
  >> "$LOG_FILE" 2>&1

log "Pruned old snapshots."

# --- Verify latest snapshot integrity ---
restic check --read-data-subset=5% >> "$LOG_FILE" 2>&1
log "Integrity check passed."

log "Backup finished successfully."
chmod +x /usr/local/bin/backup.sh

# Create password file (more secure than environment variable):
echo "your-strong-backup-password" > /root/.restic-password
chmod 600 /root/.restic-password

Schedule with Cron

crontab -e
# Run backup daily at 3am:
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup-cron.log 2>&1

# Run backup twice daily (high availability data):
0 3,15 * * * /usr/local/bin/backup.sh >> /var/log/backup-cron.log 2>&1

Schedule with Systemd Timer (preferred on systemd systems)

# /etc/systemd/system/backup.service
[Unit]
Description=Restic Backup
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=root
StandardOutput=append:/var/log/backup.log
StandardError=append:/var/log/backup.log
# /etc/systemd/system/backup.timer
[Unit]
Description=Run Restic backup daily

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800   # Spread load by up to 30 minutes
Persistent=true           # Run if system was off during scheduled time

[Install]
WantedBy=timers.target
systemctl enable --now backup.timer
systemctl status backup.timer

Part 6: Back Up PostgreSQL and MySQL

PostgreSQL

# Dump and pipe directly into Restic (no temp file):
pg_dump -U myapp myapp | restic backup --stdin \
  --stdin-filename postgres-myapp-$(date +%Y%m%d).sql \
  --tag postgres,myapp

# All databases:
pg_dumpall -U postgres | restic backup --stdin \
  --stdin-filename postgres-all-$(date +%Y%m%d).sql

MySQL / MariaDB

mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" \
  --all-databases --single-transaction \
  | restic backup --stdin \
    --stdin-filename mysql-all-$(date +%Y%m%d).sql \
    --tag mysql

Docker-Aware Backup Script

#!/bin/bash
# backup-docker-databases.sh — Dump all DBs then backup

# PostgreSQL containers:
for container in $(docker ps --filter "ancestor=postgres" --format "{{.Names}}"); do
  DB_NAME=$(docker exec "$container" env | grep POSTGRES_DB | cut -d= -f2)
  DB_USER=$(docker exec "$container" env | grep POSTGRES_USER | cut -d= -f2)

  docker exec "$container" pg_dump -U "$DB_USER" "$DB_NAME" | \
    restic backup --stdin \
      --stdin-filename "${container}-${DB_NAME}.sql" \
      --tag postgres,docker,"$container"
done

Part 7: Restore from Backup

# List all snapshots:
restic snapshots

# Output:
# ID        Time                 Host    Tags           Paths
# abc12345  2026-03-09 03:01:23  myhost  server,docker  /opt/docker, /etc

# List files in a snapshot:
restic ls abc12345

# Restore entire snapshot to /tmp/restore:
restic restore abc12345 --target /tmp/restore

# Restore latest snapshot:
restic restore latest --target /restore

# Restore specific directory from snapshot:
restic restore abc12345 --target /restore \
  --include /opt/docker/nextcloud

# Mount snapshot as filesystem (for browsing):
mkdir /mnt/restic
restic mount /mnt/restic &
ls /mnt/restic/snapshots/latest/opt/docker
# Browse and copy exactly what you need
umount /mnt/restic

Part 8: Multi-Destination Backup (3-2-1 Rule)

The 3-2-1 rule: 3 copies, 2 different storage media, 1 offsite. Achieve this with multiple repositories:

#!/bin/bash
# backup-321.sh — 3-2-1 backup strategy

# 1. Local backup (fast, for quick restores):
RESTIC_REPOSITORY="/backup/local" restic backup /opt/docker /etc

# 2. Remote B2 backup (offsite, cheap):
RESTIC_REPOSITORY="rclone:b2:backups-primary" restic backup /opt/docker /etc

# 3. Remote S3 backup (second offsite, different provider):
RESTIC_REPOSITORY="s3:s3.amazonaws.com/my-backup-bucket" restic backup /opt/docker /etc

echo "3-2-1 backup complete."

Forget and Prune per Repository

for repo in "/backup/local" "rclone:b2:backups-primary"; do
  RESTIC_REPOSITORY="$repo" restic forget \
    --keep-last 7 \
    --keep-daily 14 \
    --keep-weekly 8 \
    --prune
done

Part 9: Monitoring and Alerts

Healthchecks.io Integration

Healthchecks.io monitors cron jobs — send a ping after each successful backup, get alerted if it doesn't run:

# Add to backup.sh after successful completion:
curl -fsS --retry 3 https://hc-ping.com/your-uuid > /dev/null

# Ping on failure:
trap 'curl -fsS --retry 3 https://hc-ping.com/your-uuid/fail > /dev/null' ERR

Self-host Healthchecks.io with Docker — it's open source (BSD 3-Clause).

Backup Size Monitoring

# Check repository stats:
restic stats

# Output:
# Repository Size:  12.345 GiB
# Total File Count: 1,234,567
# Total Blob Count: 987,654
# Unique:           8.234 GiB (deduplication ratio: 1.5x)

Cost Comparison

Storage BackendCost per 100GB/monthNotes
Backblaze B2$0.60No egress within B2 CDN
Wasabi$0.59No egress fees
Cloudflare R2$1.50No egress fees
AWS S3 Standard$2.30+ egress fees
Hetzner Storage Box€0.38SFTP, 1TB for €3.81
Self-hosted (ext HDD)~$0.01Hardware cost amortized

Recommendation: Backblaze B2 for primary offsite backup, Hetzner Storage Box for secondary offsite, local SSD for immediate restore speed.


Quick Reference

# Common Restic commands:
restic init                           # Initialize repository
restic backup /path/to/data           # Create snapshot
restic snapshots                      # List snapshots
restic ls latest                      # List files in latest snapshot
restic restore latest --target /tmp   # Restore to /tmp
restic forget --keep-last 7 --prune   # Delete old snapshots
restic check                          # Verify repository integrity
restic stats                          # Show repository size/stats
restic mount /mnt/restic              # Browse as filesystem

See all open source backup tools at OSSAlt.com/categories/backup.

Comments