#!/bin/bash # LMS Second Brain — Backup Script # Backs up PostgreSQL (from Docker) + S3 files to Backblaze B2 # Place at: /opt/lms-backup/backup.sh on the server # Cron: 0 3 * * * /opt/lms-backup/backup.sh >> /var/log/lms-backup.log 2>&1 set -euo pipefail # ── Config ─────────────────────────────────────────────────────────────────── DB_CONTAINER="lms-system-db-1" DB_USER="lms_user" DB_NAME="lms_db" BACKUP_DIR="/tmp/lms-backups" DATE=$(date +%Y%m%d_%H%M) DUMP_FILE="${BACKUP_DIR}/db_${DATE}.sql.gz" # B2 rclone remote name (configured via: rclone config) B2_REMOTE="b2lms" B2_BUCKET="lms-backups-second-brain" B2_DB_PATH="${B2_REMOTE}:${B2_BUCKET}/db" B2_FILES_PATH="${B2_REMOTE}:${B2_BUCKET}/files" # Hetzner S3 rclone remote name S3_REMOTE="hetzner" S3_BUCKET="lms-uploads" # Retention: keep last N daily backups KEEP_DAYS=7 # ── Functions ───────────────────────────────────────────────────────────────── log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } # ── Main ────────────────────────────────────────────────────────────────────── log "=== LMS Backup started ===" mkdir -p "$BACKUP_DIR" # 1. PostgreSQL dump log "Dumping PostgreSQL from container ${DB_CONTAINER}..." docker exec "$DB_CONTAINER" \ pg_dump -U "$DB_USER" "$DB_NAME" \ | gzip > "$DUMP_FILE" log "Dump created: ${DUMP_FILE} ($(du -sh "$DUMP_FILE" | cut -f1))" # 2. Upload DB dump to B2 log "Uploading DB dump to Backblaze B2..." rclone copy "$DUMP_FILE" "$B2_DB_PATH" log "DB dump uploaded: ${B2_DB_PATH}/$(basename "$DUMP_FILE")" # 3. Sync S3 files to B2 log "Syncing S3 files to Backblaze B2..." rclone sync \ "${S3_REMOTE}:${S3_BUCKET}" \ "$B2_FILES_PATH" \ --progress \ --transfers=8 log "S3 files synced to ${B2_FILES_PATH}" # 4. Cleanup local temp files rm -f "$DUMP_FILE" log "Local temp files cleaned" # 5. Prune old DB backups on B2 (keep last KEEP_DAYS) log "Pruning DB backups older than ${KEEP_DAYS} days..." rclone delete "$B2_DB_PATH" \ --min-age "${KEEP_DAYS}d" \ --include "db_*.sql.gz" log "Pruning done" log "=== LMS Backup finished successfully ==="