You’re running a home server with important data — photos, documents, databases, configurations. What happens when a drive dies? When you accidentally rm -rf the wrong directory? When ransomware hits?
Restic + Rclone is the gold standard backup combo for self-hosters. Restic creates fast, encrypted, deduplicated snapshots. Rclone syncs them to any cloud provider. Together, they give you enterprise-grade backups for free.
The 3-2-1 Backup Rule
Before we start, understand the rule that prevents data loss:
- 3 copies of your data
- 2 different storage types (local + cloud)
- 1 offsite copy (cloud or remote server)
Our setup achieves all three: original data + local Restic repo + Rclone cloud sync.
What You’ll Build
- Restic — incremental, encrypted, deduplicated backups to a local repository
- Rclone — syncs the backup repo to cloud storage (Backblaze B2, S3, Google Drive, etc.)
- Automated schedule — cron job runs backups daily
- Retention policy — keeps daily/weekly/monthly snapshots automatically
Prerequisites
- Linux server (Ubuntu/Debian)
- ~100MB RAM for backup operations
- External drive or second disk for local backups (recommended)
- Cloud storage account (Backblaze B2 is cheapest at $0.005/GB/month)
Step 1: Install Restic
# Ubuntu/Debian
sudo apt install restic
# Or get the latest binary
wget https://github.com/restic/restic/releases/latest/download/restic_0.17.3_linux_arm64.bz2
bunzip2 restic_*.bz2
chmod +x restic_*
sudo mv restic_* /usr/local/bin/restic
Verify:
restic version
Step 2: Initialize the Backup Repository
Choose where to store backups locally:
# On an external drive or second disk
sudo mkdir -p /mnt/backups/restic-repo
# Initialize with a strong password
export RESTIC_PASSWORD="your-strong-password-here"
restic init --repo /mnt/backups/restic-repo
Save this password somewhere safe. Without it, your backups are unrecoverable. Consider storing it in your password manager and printing a physical copy.
Step 3: Create Your First Backup
# Backup your important directories
restic -r /mnt/backups/restic-repo backup \
/home/user/documents \
/var/www \
/opt/docker \
--exclude="*.tmp" \
--exclude="node_modules" \
--exclude=".cache"
Restic deduplicates automatically — if a file hasn’t changed, it won’t be stored again. Your second backup will be much faster than the first.
What to Back Up
| Priority | What | Why |
|---|---|---|
| Critical | Docker volumes, databases | Service data |
| Critical | /home directories | Personal files |
| High | Docker compose files | Service configs |
| High | /etc | System configuration |
| Medium | Nginx/Caddy configs | Reverse proxy setup |
| Low | Container images | Can re-pull from registries |
What NOT to Back Up
/var/lib/docker(use volume backups instead)- System packages (reinstall from apt)
- Temporary files, caches, logs
node_modules,.venv, build artifacts
Step 4: Create a Backup Script
Create /usr/local/bin/backup.sh:
#!/bin/bash
set -euo pipefail
# Configuration
export RESTIC_REPOSITORY="/mnt/backups/restic-repo"
export RESTIC_PASSWORD_FILE="/root/.restic-password"
BACKUP_PATHS=(
"/home"
"/var/www"
"/opt/docker"
"/etc/nginx"
"/etc/caddy"
)
EXCLUDE_PATTERNS=(
"node_modules"
".cache"
"*.tmp"
"*.log"
"__pycache__"
".venv"
)
LOG="/var/log/restic-backup.log"
# Build exclude args
EXCLUDES=""
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
EXCLUDES="$EXCLUDES --exclude=$pattern"
done
echo "$(date): Starting backup..." >> "$LOG"
# Stop databases before backup (optional, for consistency)
# docker stop postgres redis 2>/dev/null || true
# Run backup
restic backup ${BACKUP_PATHS[@]} $EXCLUDES \
--verbose >> "$LOG" 2>&1
# Restart databases
# docker start postgres redis 2>/dev/null || true
# Apply retention policy
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune >> "$LOG" 2>&1
echo "$(date): Backup complete" >> "$LOG"
# Check repository integrity (weekly)
if [ "$(date +%u)" -eq 7 ]; then
echo "$(date): Running integrity check..." >> "$LOG"
restic check >> "$LOG" 2>&1
fi
# Store password securely
echo "your-strong-password" > /root/.restic-password
chmod 600 /root/.restic-password
# Make script executable
chmod +x /usr/local/bin/backup.sh
Step 5: Install and Configure Rclone
# Install
curl https://rclone.org/install.sh | sudo bash
# Configure cloud storage
rclone config
Backblaze B2 Setup (Cheapest Option)
- Create a Backblaze B2 account (free)
- Create a bucket called
my-server-backups - Create an application key with access to that bucket
- In
rclone config:
Other Cloud Options
| Provider | Cost/GB/month | Rclone Name |
|---|---|---|
| Backblaze B2 | $0.005 | b2 |
| Wasabi | $0.007 | s3 |
| AWS S3 | $0.023 | s3 |
| Google Drive | Free (15GB) | drive |
| Cloudflare R2 | $0.015 (free egress) | s3 |
Step 6: Add Cloud Sync to Backup Script
Add this to the end of /usr/local/bin/backup.sh:
# Sync to cloud
echo "$(date): Syncing to cloud..." >> "$LOG"
rclone sync /mnt/backups/restic-repo b2:my-server-backups/restic \
--transfers 4 \
--fast-list \
--log-file="$LOG" \
--log-level INFO
echo "$(date): Cloud sync complete" >> "$LOG"
Step 7: Schedule with Cron
sudo crontab -e
Add:
Step 8: Docker Volume Backups
For Docker services, back up volumes properly:
#!/bin/bash
# docker-backup.sh - Run BEFORE main restic backup
BACKUP_DIR="/opt/docker-backups"
mkdir -p "$BACKUP_DIR"
# PostgreSQL
docker exec postgres pg_dumpall -U postgres > "$BACKUP_DIR/postgres.sql"
# MySQL/MariaDB
docker exec mariadb mysqldump --all-databases -u root -p"$MYSQL_ROOT_PASSWORD" > "$BACKUP_DIR/mysql.sql"
# Redis
docker exec redis redis-cli BGSAVE
sleep 2
docker cp redis:/data/dump.rdb "$BACKUP_DIR/redis.rdb"
# Generic volume backup
for vol in $(docker volume ls -q); do
docker run --rm -v "$vol:/data" -v "$BACKUP_DIR:/backup" \
alpine tar czf "/backup/vol-${vol}.tar.gz" -C /data .
done
Add to cron to run before the main backup:
Step 9: Test Your Restores
A backup you haven’t tested is not a backup.
List snapshots
restic -r /mnt/backups/restic-repo snapshots
Restore a single file
restic -r /mnt/backups/restic-repo restore latest \
--target /tmp/restore \
--include "/home/user/documents/important.pdf"
Restore everything
restic -r /mnt/backups/restic-repo restore latest \
--target /tmp/full-restore
Mount snapshots (browse like a filesystem)
mkdir /mnt/restic-mount
restic -r /mnt/backups/restic-repo mount /mnt/restic-mount
# Browse /mnt/restic-mount/snapshots/latest/
Step 10: Monitoring and Alerts
Add notification to your backup script:
# At the end of backup.sh
BACKUP_SIZE=$(restic -r "$RESTIC_REPOSITORY" stats latest --json | jq -r '.total_size')
SNAPSHOT_COUNT=$(restic -r "$RESTIC_REPOSITORY" snapshots --json | jq length)
# Discord/Slack webhook notification
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\": \"✅ Backup complete: ${SNAPSHOT_COUNT} snapshots, $(numfmt --to=iec $BACKUP_SIZE) total\"}"
Troubleshooting
“repository does not exist”
- Check the path and
RESTIC_REPOSITORYenv var - Ensure the drive is mounted:
mount | grep backups
Backup is slow
- First backup is always slow (full copy) — subsequent ones are fast
- Exclude large unnecessary files (logs, caches, media you have elsewhere)
- Use
--read-concurrency 4for faster reads
Cloud sync fails
- Run
rclone checkto verify connectivity - Check cloud storage bucket permissions
- Verify API keys haven’t expired
“wrong password”
- Restic repositories are encrypted — you need the exact password used during
init - Check
RESTIC_PASSWORD_FILEpermissions and contents
Cost Estimate
For a typical home server with 100GB of backed-up data:
| Component | Cost |
|---|---|
| Restic | Free |
| Rclone | Free |
| Local storage (1TB drive) | ~$40 one-time |
| Backblaze B2 (100GB) | $0.50/month |
| Total ongoing | $0.50/month |
Conclusion
Restic + Rclone gives you a backup system that rivals enterprise solutions — encrypted, deduplicated, versioned, and offsite — for essentially free. The hardest part is setting it up once. After that, it runs silently in the background, protecting everything you’ve built.
Don’t wait for a drive failure to wish you had backups. Set this up today. Your future self will thank you.