Introduction
In self-hosting and VPS operations, backups are your last line of defense. While rclone is great for file syncing, if you need versioned backups, encrypted storage, and incremental deduplication, Restic is one of the most elegant open-source solutions available today.
This guide walks you through building a complete Restic backup system from scratch, encrypting and backing up data to S3-compatible storage (MinIO, AWS S3, Cloudflare R2 all work), with crontab for fully automated, unattended operation.
Why Restic?
| Feature | Restic | rclone copy | rsync |
|---|---|---|---|
| Incremental backup | ✅ Block-level dedup | ❌ Full copy | ❌ File-level |
| End-to-end encryption | ✅ Built-in AES-256 | ❌ Needs extra config | ❌ |
| Snapshot management | ✅ Automatic versioning | ❌ | ❌ |
| Native S3/R2 support | ✅ | ✅ | ❌ |
| Cross-platform | Linux/macOS/Windows | All platforms | Linux/macOS |
| Restore granularity | File/dir/volume | File/dir | File/dir |
Key advantage: Restic performs block-level deduplication at the repository level. A 1TB disk image might only upload a few MB on subsequent backups. Combined with encryption, your backups remain absolutely secure even on public cloud storage.
Architecture Overview
┌──────────────────────────────────────────────────┐
│ VPS (Source Server) │
│ │
│ ┌────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ /etc │ │ /home │ │ DB dumps │ │
│ │ Config │ │ User data │ │ (mysqldump) │ │
│ └────┬────┘ └────┬─────┘ └───────┬───────┘ │
│ │ │ │ │
│ └──────────┬──┴─────────────────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Restic CLI │ Encrypt + Dedup + Snap │
│ └───────┬───────┘ │
│ │ HTTPS │
└─────────────────┼──────────────────────────────────┘
│
┌───────▼────────┐
│ S3 Storage │
│ (MinIO / R2) │
│ Encrypted repo │
└────────────────┘
Environment Setup
Install Restic
# Ubuntu/Debian
wget https://github.com/restic/restic/releases/latest/download/restic_0.17.0_$(dpkg --print-architecture).deb
sudo dpkg -i restic_0.17.0_$(dpkg --print-architecture).deb
# Or build from source
git clone https://github.com/restic/restic.git
cd restic && go build && sudo cp restic /usr/local/bin/
# Verify installation
restic version
# restic 0.17.0 compiled at 2024-12-18 19:29:04 UTC using go1.23.4
Deploy S3-Compatible Storage (MinIO)
If your VPS has enough resources, deploy MinIO locally:
# Single-binary MinIO deployment
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
# Create data directory
sudo mkdir -p /data/minio
sudo useradd -r -s /bin/false minio
sudo chown minio:minio /data/minio
# Create systemd service
sudo tee /etc/systemd/system/minio.service > /dev/null <<'EOF'
[Unit]
Description=MinIO Object Storage
After=network-online.target
[Service]
User=minio
Group=minio
ExecStart=/usr/local/bin/minio server /data/minio \
--console-address ":9001" \
--address ":9000"
Restart=always
Environment="MINIO_ROOT_USER=admin"
Environment="MINIO_ROOT_PASSWORD=your...sudo systemctl daemon-reload
sudo systemctl enable --now minio
Visit http://<your-vps-ip>:9001 to create a bucket like restic-backups.
Alternative: If you don’t want to run MinIO, use Cloudflare R2 (zero egress fees) or AWS S3 directly.
Configure S3 Environment Variables
# Add to ~/.bashrc or /etc/environment
export S3_ENDPOINT="http://127.0.0.1:9000" # MinIO address
export S3_ACCESS_KEY="admin"
export S3_SECRET_KEY="your...port RESTIC_REPOSITORY="s3:s3.amazonaws.com/restic-backups"
export RESTIC_PASSWORD="your...n
# Apply immediately
source ~/.bashrc
For Cloudflare R2:
export S3_ENDPOINT="https://s3.us-east-1.r2.cloudflarestorage.com"
export S3_ACCESS_KEY="<r2-access-key>"
export S3_SECRET_KEY="<r2-...port RESTIC_REPOSITORY="s3:s3.us-east-1.r2.cloudflarestorage.com/restic-backups"
Initializing Repository & First Backup
Initialize Repository
restic init
# created restic repository <repo-id> at ...
# password protection enabled
Backup Critical Directories
# Backup multiple paths
restic backup /etc /home /var/www --tag vps-config --tag production
# View snapshots
restic snapshots
# ID Date Host Tags Paths
# a1b2c3d4 2026-07-25 03:00:12 vps01 vps-config /etc /home /var/www
# 4 objects stored, has been excluded from listing
# 5 objects stored, new size: 2.345 GiB
# Check repository stats
restic stats
# total files: 12847
# total bytes: 2.345 GiB
# unique data: 892 MiB ← actual storage after dedup
# compressed: 1.123 GiB
Database-Specific Backups
Restic backs up at the file level; databases should be dumped first:
#!/bin/bash
# /usr/local/bin/db-backup.sh
set -euo pipefail
BACKUP_DIR="/tmp/restic-db-dump"
mkdir -p "$BACKUP_DIR"
# MySQL/MariaDB
for db in $(mysql -e 'SHOW DATABASES;' -N | grep -v '^Information\|^performance'); do
mysqldump --single-transaction --routines --triggers "$db" \
> "$BACKUP_DIR/${db}_$(date +%Y%m%d_%H%M%S).sql"
done
# PostgreSQL
pg_dumpall > "$BACKUP_DIR/all_pg_$(date +%Y%m%d_%H%M%S).sql"
# Backup dump files with Restic
restic backup "$BACKUP_DIR" --tag database-dumps
rm -rf "$BACKUP_DIR"
echo "$(date): Database backups completed successfully" >> /var/log/db-backup.log
sudo chmod +x /usr/local/bin/db-backup.sh
Automated Scheduled Backups
Crontab Configuration
# Create dedicated backup script
sudo tee /usr/local/bin/restic-auto-backup.sh > /dev/null <<'SCRIPT'
#!/bin/bash
set -euo pipefail
LOG="/var/log/restic-backup.log"
LOCKFILE="/tmp/restic-backup.lock"
# Prevent concurrent execution
exec 200>"$LOCKFILE"
flock -n 200 || { echo "$(date): Another backup is running, exiting." >> "$LOG"; exit 0; }
echo "=== Backup started at $(date) ===" >> "$LOG"
# Pre-step: database dump
/usr/local/bin/db-backup.sh >> "$LOG" 2>&1
# Execute Restic backup
restic backup \
/etc \
/home \
/var/www \
/tmp/restic-db-dump \
--tag auto-backup \
--tag "$(date +%Y-%m)" \
>> "$LOG" 2>&1
# Clean old snapshots: keep 7 daily, 4 weekly, 12 monthly
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune >> "$LOG" 2>&1
echo "=== Backup completed at $(date) ===" >> "$LOG"
rm -rf /tmp/restic-db-dump
# Release lock
flock -u 200
SCRIPT
sudo chmod +x /usr/local/bin/restic-auto-backup.sh
# Edit crontab
crontab -e
# Daily backup at 3 AM with email notification
0 3 * * * /usr/local/bin/restic-auto-backup.sh
Monitoring & Alerts
# Check backup health
restic check --read-data-store >> /var/log/restic-health.log 2>&1
# Set up health check cron
0 6 * * * restic snapshots --last 1 | tail -1 >> /var/log/restic-status.log 2>&1
Recovery Operations
List All Snapshots
restic snapshots
restic snapshots --tag auto-backup
Restore Individual Files
# Restore to specified directory
restic restore latest --target /tmp/recovered/
# Restore specific path
restic restore a1b2c3d4 --include "/etc/nginx/nginx.conf" --target /tmp/
# Restore snapshot with specific tag
restic restore --tag production --target /tmp/prod-recovery/
Full System Recovery
# Mount as FUSE (browse in real-time)
sudo apt install restic-fuse # available on some distros
restic mount /mnt/restic &
ls /mnt/restic/
# Boot from ISO for full disk recovery
# Mount the backup target drive, then:
restic restore latest --target /mnt/recovery/
Best Practices & Optimization
Exclude Unnecessary Content
restic backup /home --exclude '*.cache' --exclude '.local/share/Trash' --exclude 'node_modules'
Repository Health Checks
# Run weekly
restic check --quiet
# No output = healthy
# Force rebuild index
restic rebuild-index
Multi-Repository Strategy
Production → R2 (cold backup, monthly)
Development → MinIO (hot backup, daily)
Personal data → Local NAS (on-premise)
Password Management
# Never put passwords in scripts! Use a key manager
export RESTIC_PASSWORD_FILE=/run/s...d
# Or
export RESTIC_PASSWORD_KEYRING=*** # use libsecret/keyring
Cost Estimation
| Option | Storage Cost | Monthly Estimate |
|---|---|---|
| MinIO (local) | Hardware cost | $0 (existing disk) |
| Cloudflare R2 | Free 10GB, then $0.015/GB | ~$1.50/mo (1TB) |
| AWS S3 Standard | $0.023/GB | ~$2.50/mo (1TB) |
| Backblaze B2 | $0.005/GB | ~$0.70/mo (1TB) |
Restic’s deduplication means: For 100GB of daily backup with only 2% daily change, you only need about 6GB of additional storage per month.
Summary
Restic + S3 provides VPS operators with:
- Encrypted security: AES-256 end-to-end encryption; cloud providers can’t read your data
- Extreme deduplication: Block-level dedup saves 70-90% storage space
- Version history: Unlimited snapshots, rollback to any point in time
- Automation-friendly: One command trigger, perfect for crontab integration
📦 Next step: Configure Restic automated backups for your VPS, then perform a recovery test—backups that haven’t been verified by restoration are no backups at all.
