Files
homelab-configs/nextcloud-backup-to-backblaze.sh
T
jgitta 96bd803880 Fix: Use NFS warm-storage for backup staging instead of data disk
CRITICAL FIX:
The previous implementation used /mnt/nextcloud-data for staging, which is on
the same 2TB disk as the 1.4TB Nextcloud data. This caused disk full errors:
  1.4TB (data) + 587GB (compressed backup) = 1.987TB > 2TB limit

Solution:
Use /mnt/warm-storage (NFS mount with 900GB free) for backup staging:
  • Sufficient space for 587GB backup file
  • Separate storage from Nextcloud data disk
  • NFS performance acceptable for backup writes
  • Preserves data disk for actual Nextcloud usage

Impact:
- Backup process will no longer fail due to lack of staging space
- Complete 1.4TB backups can now succeed
- Files can be uploaded to B2 without blocking on disk space

Tested: Backup successfully compressed 1.4TB to 587GB before hitting old limit
2026-04-25 21:20:47 -05:00

94 lines
3.7 KiB
Bash
Executable File

#!/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}"