A resilient, multi-site automated backup manager for Linux VPS environments. It handles PostgreSQL database dumps, integrity verification, Cloudflare R2 media sync, local asset folder/CDN synchronization or archiving, automated log rotation, modern public HTML status dashboards for Caddy/Nginx, and dual notifications via Discord and Telegram.
- Per-Site Rclone Remotes: Every site can define its own dedicated Google Drive, Nextcloud, or R2 remotes (
SITE<N>_REMOTE_*), completely isolating credentials and destination buckets across projects. - Granular Remote Retention: Configure a general retention period (
SITE<N>_DB_REMOTE_RETENTION_DAYS) or dedicated per-provider days (SITE<N>_DB_REMOTE_RETENTION_DAYS_GDRIVE,SITE<N>_DB_REMOTE_RETENTION_DAYS_NEXTCLOUD). - Optional Versioned Trash for Folders & CDN: When syncing local asset folders, versioned date-stamped trash bins (
SITE<N>_FOLDER_TRASH_PATH_*) are completely optional. If omitted, standard direct synchronization is performed. - Multi-Site Discovery: Automatically scans and processes any number of sites configured as
SITE<N>_*in.envwithout modifying code. - Local Folders & CDN Backups: Supports backing up local asset directories (e.g. CDN folders, uploads) using either high-performance incremental sync with dated trash (
rclone sync --backup-dir) or compressed versioned archives (tar.gzorzip). - PostgreSQL Dumps & Verification: Generates custom compressed PostgreSQL dumps (
.dump) and verifies archive integrity usingpg_restore --listbefore starting uploads. - Credential Protection: Database passwords are automatically parsed from connection URIs and supplied via
PGPASSWORDat runtime, preventing plain-text password exposure inps auxor/proc/<pid>/cmdline. - Cloudflare R2 Anti-Wipe Protection: Checks bucket size and file counts before performing
rclone sync. If the source bucket is empty or unreachable, the sync aborts immediately to prevent wiping remote backups. - Versioned Trash Retention (
--backup-dir): Preserves overwritten or deleted files in dated trash folders before pruning them according to retention policies. - Automated Log Rotation: Prevents log files from consuming VPS disk space by automatically truncating older entries when exceeding configurable size thresholds (
BACKUP_LOG_MAX_MB). - Live HTML Status Dashboard: Generates a self-contained, responsive dark-mode HTML dashboard (
generate_report.py) with real-time log search and system health indicators, ready to be served publicly in read-only mode by Caddy or Nginx. - Dual Alerts (Discord & Telegram): Delivers rich embed status reports to Discord channels and instant alerts + full log document delivery to Telegram bots.
- Concurrency Lock (
flock): Prevents overlapping backup cron executions using Linux file descriptors.
Ensure the following tools are installed on your Linux system:
| Tool | Purpose |
|---|---|
bash (>= 4.4) |
Script execution with set -Eeuo pipefail |
rclone |
Cloud storage sync and copy operations |
pg_dump & pg_restore |
PostgreSQL database dumping and integrity checks |
python3 (>= 3.8) |
HTML status dashboard rendering |
curl |
Discord and Telegram webhook delivery |
jq |
Building JSON payloads for Discord embeds |
flock |
Concurrency locking |
find |
Local dump cleanup and retention management |
tar & gzip |
Standard Unix compression for folder archive mode |
zip |
Optional zip compression for folder archive mode |
On Debian/Ubuntu, install the core dependencies via:
sudo apt-get update
sudo apt-get install -y postgresql-client rclone curl jq util-linux python3 tar zipbackup_manager/
├── run_backup.sh # Main backup orchestrator script
├── generate_report.py # Self-contained HTML status dashboard generator
├── .env.example # Environment configuration template
├── .env # Private configuration file (git-ignored)
└── logs/
├── backup.log # System log file (auto-rotated)
└── index.html # Public read-only dashboard served by Caddy
Copy the configuration template:
cp .env.example .env
chmod 600 .env
chmod +x run_backup.sh generate_report.pyBACKUP_LOG_FILE="/home/felt/backup_manager/logs/backup.log"
BACKUP_LOG_MAX_MB=10
BACKUP_HTML_FILE="/home/felt/backup_manager/logs/index.html"
# Telegram Notifications
TELEGRAM_BOT_TOKEN="your_bot_token_here"
TELEGRAM_CHAT_ID="your_chat_id_here"
TELEGRAM_SEND_LOG_DOCUMENT=trueSites are declared dynamically with numerical suffixes (SITE1, SITE2, etc.):
# Site 1: Production Database + Cloudflare R2
SITE1_ENABLE=true
SITE1_NAME="production_site"
SITE1_DB_URL="postgres://user:password@127.0.0.1:5432/production_db"
SITE1_REMOTE_GDRIVE="production_gdrive"
SITE1_REMOTE_NEXTCLOUD="nextcloud"
SITE1_REMOTE_R2="production_r2"
SITE1_DB_LOCAL_DIR="/home/felt/backup_manager/dumps/production_site"
SITE1_DB_PATH_GDRIVE="backups/production/database"
SITE1_DB_PATH_NEXTCLOUD="backups/production/database"
SITE1_R2_ENABLE=true
SITE1_R2_BUCKET="production-bucket"
SITE1_R2_PATH_GDRIVE="backups/production/r2"
SITE1_R2_TRASH_PATH_GDRIVE="backups/production/r2_trash"
SITE1_DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."
# Site 2: Database + Local CDN / Assets Folder
SITE2_ENABLE=true
SITE2_NAME="assets_site"
SITE2_DB_URL="postgres://user:password@127.0.0.1:5432/assets_db"
SITE2_REMOTE_GDRIVE="assets_drive"
SITE2_REMOTE_NEXTCLOUD="nextcloud"
SITE2_DB_LOCAL_DIR="/home/felt/backup_manager/dumps/assets_site"
SITE2_DB_PATH_GDRIVE="backups/assets/database"
SITE2_DB_PATH_NEXTCLOUD="backups/assets/database"
SITE2_FOLDER_ENABLE=true
SITE2_FOLDER_SRC="/var/www/assets_site/cdn"
SITE2_FOLDER_PATH_GDRIVE="backups/assets/cdn"
SITE2_FOLDER_PATH_NEXTCLOUD="backups/assets/cdn"
SITE2_FOLDER_MODE="sync" # "sync" for incremental mirror or "archive" for tar.gz/zip
SITE2_FOLDER_TRASH_PATH_GDRIVE="backups/assets/cdn_trash"
SITE2_DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."To publish the read-only status dashboard publicly, add a simple virtual host to your Caddyfile:
status.yourdomain.com {
root * /home/felt/backup_manager/logs
file_server
}Then reload Caddy:
sudo systemctl reload caddyManual execution:
./run_backup.shScheduled execution via crontab:
crontab -eAdd a daily schedule (e.g. every night at 03:00 AM):
0 3 * * * /home/felt/backup_manager/run_backup.sh >/dev/null 2>&1
MIT License. Free for personal and commercial use.