The 3-2-1 Backup Architecture: Automated Encrypted Restic Backups to B2 & S3 with Systemd Timers

3 min read

Relying solely on RAID redundancy or local snapshots is not a backup strategy. Drive failures, controller corruption, physical disasters, or ransomware require a hardened, automated 3-2-1 backup architecture: 3 copies of your data, across 2 different storage media, with 1 copy stored immutable and offsite.

Restic is the modern enterprise gold standard for Linux backups. Featuring client-side ChaCha20-Poly1305 encryption, content-defined chunk deduplication, and native support for Backblaze B2 and AWS/Wasabi S3, Restic ensures your data remains secure and recoverable. This guide details repository initialization, automated production systemd timers with jitter, retention pruning, and weekly integrity checks.

⚡ Quick Runbook: 60-Second Implementation

TL;DR: Deploy a production Restic backup automation script executed via systemd timers:

# 1. Install Restic
sudo apt update && sudo apt install -y restic

# 2. Configure credentials file (/etc/restic/b2-backup.env)
sudo mkdir -p /etc/restic && sudo chmod 700 /etc/restic
sudo tee /etc/restic/b2-backup.env << 'EOF'
RESTIC_REPOSITORY="b2:my-homelab-backup-bucket:servers/prod-01"
RESTIC_PASSWORD_FILE="/etc/restic/password.txt"
B2_ACCOUNT_ID="your-b2-key-id"
B2_ACCOUNT_KEY="your-b2-application-key"
EOF
sudo chmod 600 /etc/restic/b2-backup.env

# 3. Create strong password
openssl rand -base64 32 | sudo tee /etc/restic/password.txt > /dev/null
sudo chmod 600 /etc/restic/password.txt

# 4. Initialize the encrypted remote repository
set -a && source /etc/restic/b2-backup.env && set +a
restic init

1. The 3-2-1 Backup Rule for Self-Hosted Servers

  • 3 Copies of Data: The live production data on your server, a fast local snapshot on an independent local storage pool (e.g. TrueNAS fastpool/datapool), and an encrypted remote offsite copy.
  • 2 Different Media Types: Local high-speed NVMe/HDD storage + remote cloud object storage (Backblaze B2, Cloudflare R2, or Wasabi S3).
  • 1 Offsite Immutable Copy: Encrypted client-side before transmission. Even if cloud credentials are intercepted, your data cannot be decrypted without your private passphrase.

2. Production Automated Backup Script

Create /usr/local/bin/restic-backup.sh to perform database dumps, snapshot creation, retention pruning, and notification dispatch:

#!/usr/bin/env bash
set -euo pipefail

source /etc/restic/b2-backup.env

# Backup critical directories and Docker volumes
restic backup     --exclude-caches     --exclude "/var/lib/docker/overlay2"     --exclude "**/node_modules"     /etc     /opt/stacks     /mnt/fastpool/appdata

# Enforce retention policy: Keep 7 daily, 4 weekly, 12 monthly snapshots
restic forget     --keep-daily 7     --keep-weekly 4     --keep-monthly 12     --prune

# Perform weekly consistency verification
if [ "$(date +%u)" -eq 7 ]; then
    restic check --read-data-subset=5%
fi

# Send success ping to Uptime Kuma push monitor
curl -fsS -m 10 --retry 3 https://uptime.example.com/api/push/RESTIC_BACKUP_KEY > /dev/null 2>&1

Make the script executable: sudo chmod 700 /usr/local/bin/restic-backup.sh.


3. Scheduling with Native Systemd Timers

Modern Linux servers avoid classic cron in favor of systemd timers, which provide execution logging, resource limits, and randomized delay (jitter) to prevent hammering cloud storage APIs:

# /etc/systemd/system/restic-backup.service
[Unit]
Description=Automated Restic Cloud Backup
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup.sh
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Trigger Restic Backup Nightly at 02:30 AM

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

[Install]
WantedBy=timers.target
# Reload systemd and enable timer
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer

4. Snapshot Restoration Runbook

In a disaster recovery scenario, restore snapshots directly onto a clean host:

# List available snapshots
restic snapshots

# Restore latest snapshot to /tmp/restore
restic restore latest --target /tmp/restore

Related Runbooks in This Series

Tags:

← Previous:
Next: →