#!/bin/bash ################################################################################ # Nextcloud to Backblaze B2 Backup Script - Native Installation ################################################################################ set -euo pipefail # Source credentials source /etc/nextcloud-backup/b2-credentials.env || exit 1 source /opt/nextcloud-backup/.env || exit 1 # B2 CLI path B2="/root/.local/share/pipx/venvs/b2/bin/b2" # Configuration BACKUP_DIR="/opt/nextcloud-backup/.backups" LOG_DIR="${BACKUP_DIR}/logs" TIMESTAMP=$(date +%Y%m%d_%H%M%S) LOG_FILE="${LOG_DIR}/backup_${TIMESTAMP}.log" # Use warm-storage NFS mount for staging (900GB available, separate from data disk) # /mnt/nextcloud-data is 2TB total with 1.4TB data = can't fit 587GB backup # /mnt/warm-storage is 900GB NFS with 0% used = sufficient space STAGING_DIR="/mnt/warm-storage/.backup-staging" TEMP_DIR="${STAGING_DIR}/nextcloud-backup-${TIMESTAMP}" # Create directories mkdir -p "${BACKUP_DIR}" "${LOG_DIR}" "${TEMP_DIR}" # Backup database echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📦 Backing up Nextcloud database..." | tee -a "${LOG_FILE}" DB_BACKUP="${TEMP_DIR}/nextcloud-db-${TIMESTAMP}.sql.zst" if mysqldump -u"${NEXTCLOUD_DB_USER}" -p"${NEXTCLOUD_DB_PASSWORD}" \ --single-transaction --lock-tables=false \ "${NEXTCLOUD_DB_NAME}" 2>/dev/null | zstd -T0 > "$DB_BACKUP"; then DB_SIZE=$(du -h "$DB_BACKUP" | cut -f1) echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Database backup: $DB_SIZE" | tee -a "${LOG_FILE}" else echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ Database backup failed" | tee -a "${LOG_FILE}" rm -rf "$TEMP_DIR" exit 1 fi # Backup files echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📦 Backing up Nextcloud files..." | tee -a "${LOG_FILE}" FILES_BACKUP="${TEMP_DIR}/nextcloud-files-${TIMESTAMP}.tar.zst" if tar --zstd -cf "$FILES_BACKUP" \ -C "${NEXTCLOUD_DATA_PATH}" . \ --exclude-caches \ --warning=no-file-removed 2>/dev/null; then FILES_SIZE=$(du -h "$FILES_BACKUP" | cut -f1) echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Files backup: $FILES_SIZE" | tee -a "${LOG_FILE}" else echo "[$(date '+%Y-%m-%d %H:%M:%S')] ⚠️ Files backup skipped" | tee -a "${LOG_FILE}" fi # Upload to B2 echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📤 Uploading to Backblaze B2..." | tee -a "${LOG_FILE}" if "${B2}" account get >/dev/null 2>&1; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ B2 authenticated" | tee -a "${LOG_FILE}" # Upload database if [ -f "$DB_BACKUP" ]; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📤 Uploading database..." | tee -a "${LOG_FILE}" if "${B2}" file upload "${B2_BUCKET_NAME}" "$DB_BACKUP" "nextcloud-backups/$(basename $DB_BACKUP)" >>/dev/null 2>&1; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Database uploaded" | tee -a "${LOG_FILE}" else echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ Database upload failed" | tee -a "${LOG_FILE}" fi fi # Upload files if [ -f "$FILES_BACKUP" ]; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📤 Uploading files..." | tee -a "${LOG_FILE}" if "${B2}" file upload "${B2_BUCKET_NAME}" "$FILES_BACKUP" "nextcloud-backups/$(basename $FILES_BACKUP)" >>/dev/null 2>&1; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Files uploaded" | tee -a "${LOG_FILE}" else echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ Files upload failed" | tee -a "${LOG_FILE}" fi fi else echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ B2 authentication failed" | tee -a "${LOG_FILE}" rm -rf "$TEMP_DIR" exit 1 fi # Cleanup rm -rf "$TEMP_DIR" rmdir "$STAGING_DIR" 2>/dev/null || true # Remove staging directory if empty echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Backup completed!" | tee -a "${LOG_FILE}"