This comprehensive guide provides a complete, secure, and fully automated backup solution for Proxmox VE using rclone with client-side encrypted iDrive E2 cloud storage. The system is designed for maximum reliability, performance, and security.
Key Features:
- Monthly backup cycle with automatic retention (max. 5 months)
- Retention runs only after a successful upload and content verification
- Full AES-256-CTR encryption with HMAC-SHA256 integrity
- High-performance upload with 8 parallel transfers and 64M chunks
- Background execution via
screenwith live progress monitoring - Comprehensive logging and detailed email reports
- Lockfile protection against concurrent runs
- Optional dual-WAN routing for dedicated upload bandwidth
Script 1: rclone_backup.sh – Core Backup Engine (FINAL VERSION)
Purpose: Fully automated monthly backup with encryption, retention, and monitoring.
Key Improvements Over Basic Scripts:
- Safe Retention: Validates the source, uploads and verifies files before deleting old months
- Live Status: Logs upload progress and each old month removed
- Robust Error Handling: Stops on upload, verification or retention errors
- Listing Validation: Remote listing errors abort retention
- Performance Optimized: 8 transfers, 16 checkers, 64M chunks, 256M buffer
- Long-Run Protection: Warning email if upload exceeds 24 hours
- Final Verification: Downloads and compares selected files through the encrypted remote
#!/usr/bin/env bash
set -euo pipefail
umask 077
# Configuration: run this complete script inside screen if desired.
logfile="/var/log/rclone_backup.log"
lockfile="/var/run/rclone_backup.lock"
email="mail@server.de"
backup_source="/mnt/USBBackup/dump/"
backup_target="idrive-enc:"
retention_limit=5
max_expected_hours=24
current_date=$(date +%Y-%m)
month_tag=$(date +%Y_%m)
log() { printf '[%s] %s\n' "$(date '+%F %T')" "$1" | tee -a "$logfile"; }
for cmd in rclone flock find sort; do
command -v "$cmd" >/dev/null || { echo "Missing dependency: $cmd" >&2; exit 1; }
done
[[ -n "$backup_target" ]] || exit 1
exec 9>"$lockfile"
flock -n 9 || { log "Another backup is already running."; exit 1; }
# Never unlink the lock file: all processes must lock the same inode.
work_dir=$(mktemp -d)
upload_pid=''
cleanup() {
local status=$?
trap - EXIT
if [[ -n "$upload_pid" ]]; then
kill "$upload_pid" 2>/dev/null || true
wait "$upload_pid" 2>/dev/null || true
fi
[[ ! -f "$work_dir/upload.log" ]] || cat "$work_dir/upload.log" >> "$logfile" || true
rm -rf -- "$work_dir"
exit "$status"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
# Validate the source before creating or deleting anything remotely.
cd "$backup_source" || { log "Source directory inaccessible."; exit 1; }
find . -type f -name "*$month_tag*.zst" -printf '%P\n' > "$work_dir/files"
if [[ ! -s "$work_dir/files" ]]; then
log "No backups for $month_tag; remote backups left unchanged."
exit 0
fi
target_path="$backup_target$current_date"
log "Uploading backups to $target_path"
rclone copy . "$target_path" --files-from-raw "$work_dir/files" \
--log-file "$work_dir/upload.log" --log-level INFO \
--transfers 8 --checkers 16 --s3-chunk-size 64M --buffer-size 256M \
--retries 10 --low-level-retries 20 --timeout 5m --stats 10s &
upload_pid=$!
started=$SECONDS
warned=0
while kill -0 "$upload_pid" 2>/dev/null; do
if (( SECONDS - started >= max_expected_hours * 3600 && warned == 0 )); then
warned=1
log "WARNING: Upload running longer than $max_expected_hours hours."
printf 'To: %s\nSubject: Backup taking too long\n\nCheck %s\n' "$email" "$logfile" \
| /usr/sbin/sendmail -t -i || log "Warning email failed."
fi
sleep 5
done
upload_status=0
wait "$upload_pid" || upload_status=$?
upload_pid=''
if (( upload_status != 0 )); then
log "Upload failed (exit $upload_status); retention skipped."
exit "$upload_status"
fi
# Verify the selected files through the decrypted remote before retention.
# --download compares content even when encrypted remote hashes are unavailable.
if ! rclone check . "$target_path" --files-from-raw "$work_dir/files" --one-way --download; then
log "Verification failed; retention skipped."
exit 1
fi
# A failed listing must never be treated as an empty/successful result.
rclone lsf --dirs-only "$backup_target" > "$work_dir/directories"
sed -nE 's#^([0-9]{4}-(0[1-9]|1[0-2]))/$#\1#p' "$work_dir/directories" | sort -r > "$work_dir/months"
mapfile -t months < "$work_dir/months"
for ((i=retention_limit; i<${#months[@]}; i++)); do
month=${months[$i]}
[[ "$month" < "$current_date" ]] || continue
log "Removing old backup month $month after successful verification."
rclone delete "$backup_target$month/" --rmdirs
done
log "Backup upload and verification completed."
printf 'To: %s\nSubject: Backup completed\n\nVerified backup: %s\n' "$email" "$target_path" \
| /usr/sbin/sendmail -t -i
Script 2: rclone_backup_dualwan.sh – Dedicated Upload Line (Optional)
Use Case: Route backup traffic through a secondary WAN interface (e.g., 4G/5G) to avoid congesting primary internet.
Requirements:
- Secondary gateway reachable (e.g.,
192.168.123.2) - Interface
vmbr0or similar - Routing table
200 secondgwin/etc/iproute2/rt_tables
echo "200 secondgw" >> /etc/iproute2/rt_tables#!/bin/bash
# =============================================================================
# Dual-WAN Backup Router – Routes rclone traffic via secondary gateway
# =============================================================================
set -euo pipefail
# Root check
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root."
exit 1
fi
# === CONFIG ===
SECOND_GW="192.168.123.2" # ← Secondary gateway IP
TABLE_NAME="secondgw"
TABLE_ID="200"
RCLONE_BACKUP_SCRIPT="/usr/local/bin/rclone_backup.sh"
DEVICE="vmbr0" # ← Your network interface
logfile="/var/log/rclone_backup_dualwan.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$logfile"
}
log "===== Starting Dual-WAN Backup via $SECOND_GW ====="
# Ensure routing table exists
if ! grep -q "^$TABLE_ID[[:space:]]\+$TABLE_NAME" /etc/iproute2/rt_tables; then
echo "$TABLE_ID $TABLE_NAME" >> /etc/iproute2/rt_tables
log "Routing table '$TABLE_NAME' (ID $TABLE_ID) created."
fi
# Setup routing
ip route flush table "$TABLE_NAME" || true
ip route add default via "$SECOND_GW" dev "$DEVICE" table "$TABLE_NAME"
ip rule add fwmark 0x1 table "$TABLE_NAME"
iptables -t mangle -A OUTPUT -p tcp --dport 443 -m owner --uid-owner root -j MARK --set-mark 1
log "Starting backup via secondary gateway..."
if bash "$RCLONE_BACKUP_SCRIPT"; then
log "Backup completed successfully."
else
log "Backup failed!"
fi
# Cleanup
log "Removing temporary routing rules..."
iptables -t mangle -D OUTPUT -p tcp --dport 443 -m owner --uid-owner root -j MARK --set-mark 1 || true
ip rule del fwmark 0x1 table "$TABLE_NAME" || true
ip route flush table "$TABLE_NAME" || true
log "Dual-WAN backup completed."Rclone Configuration – Encrypted Remote
File: ~/.config/rclone/rclone.conf
[idrive-e2]
type = s3
provider = IDrive
access_key_id = YOUR_ACCESS_KEY_ID
secret_access_key = YOUR_SECRET_ACCESS_KEY
acl = private
endpoint = node.nl32.idrivee2-3.com
bucket_acl = private
[idrive-enc]
type = crypt
remote = idrive-e2:proxmox-backups
filename_encryption = off
directory_name_encryption = false
password = YOUR_CRYPT_PASSWORDSecurity Notes:
- Encryption: AES-256-CTR with HMAC-SHA256
- Password: 64 characters → >10^50 years to crack
- Filename Encryption: Off → readable logs, encrypted content
- Salt: Not needed (scrypt built-in)
Restore from iDrive E2
Step-by-Step Restore:
- List files:
Bash rclone ls idrive-enc:2025-06 - Download backup:
Bash rclone copy idrive-enc:2025-06/vzdump-qemu-100-2025_06_01.vma.zst /var/lib/vz/dump/ - Restore VM:
Bash qmrestore /var/lib/vz/dump/vzdump-qemu-100-2025_06_01.vma.zst 100
Integrity Check (Recommended):
zstd -t /var/lib/vz/dump/vzdump-qemu-100-2025_06_01.vma.zstCronjob – Automated Monthly Execution
Recommended: Run on the 1st of each month at 02:00 AM
0 2 1 * * /usr/local/bin/rclone_backup.sh >> /var/log/rclone_backup_cron.log 2>&1Alternative (with screen):
0 3 1 * * /usr/bin/screen -dmS monthly-backup /usr/local/bin/rclone_backup.shPerformance & Security Summary
| Feature | Implementation |
|---|---|
| Encryption | AES-256-CTR + HMAC-SHA256 |
| Password Strength | 64 chars → uncrackable |
| Retention | Max 5 months, safe deletion |
| Speed | 8 transfers, 64M chunks, 256M buffer |
| Reliability | 10 retries, 5m timeout, lockfile |
| Monitoring | Optional screen + email + logs |
Troubleshooting
- Check logs:
tail -f /var/log/rclone_backup.log - Live progress:
tail -f /var/log/rclone_backup.log; when started with the example screen cronjob, usescreen -r monthly-backup - Test encryption:
rclone cat idrive-enc:2025-11/test.txt - List buckets:
rclone lsf idrive-enc:
Final Notes
- Automated and encrypted; test backup and restore with your own configuration
- Works with Proxmox Backup Server or manual
vzdump - Zero downtime – runs in background
- Full audit trail with logs and emails
- Optional dual-WAN for dedicated bandwidth
Your Proxmox data is now protected with verified uploads and post-upload retention.