8faba05b5b
Deploy Claude Dev (Dev) / deploy-claude-dev-dev (push) Failing after 2m3s
Deploy Dashboard (Main) / Backend Tests (push) Successful in 3m7s
Deploy Dashboard (Main) / Frontend Tests (push) Successful in 6s
Deploy Dashboard (Main) / Deploy to Production (push) Has been cancelled
Deploy Dashboard (Main) / Build Main Image (push) Has been cancelled
Deploy Dashboard (Dev) / Backend Tests (push) Successful in 1m18s
Deploy Dashboard (Dev) / Frontend Tests (push) Successful in 51s
Deploy Dashboard (Dev) / Build Dev Image (push) Successful in 1m42s
Deploy Dashboard (Dev) / Deploy to Dev (push) Successful in 58s
92 lines
2.8 KiB
Bash
Executable File
92 lines
2.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# NAS Backup Script — runs daily via cron
|
|
# Crontab: 0 3 * * * /volume1/docker/nas-tools/backup.sh >> /volume1/backups/nas-tools/backup.log 2>&1
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
BACKUP_DIR="/volume1/backups/nas-tools"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
RETENTION_DAYS=7
|
|
ERRORS=0
|
|
|
|
[ -f "$SCRIPT_DIR/.env" ] && source "$SCRIPT_DIR/.env"
|
|
|
|
# Source cloud backup extension (Phase 13 — Off-site Backup)
|
|
CLOUD_ENABLED="${CLOUD_ENABLED:-false}"
|
|
if [ "$CLOUD_ENABLED" = "true" ]; then
|
|
RCLONE_CONFIG_DIR="${RCLONE_CONFIG_DIR:-$SCRIPT_DIR/rclone}"
|
|
CLOUD_RETENTION_DAYS="${CLOUD_RETENTION_DAYS:-30}"
|
|
source "$SCRIPT_DIR/scripts/cloud-backup.sh"
|
|
fi
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }
|
|
|
|
notify() {
|
|
if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
|
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
|
-d chat_id="$TELEGRAM_CHAT_ID" -d text="$1" -d parse_mode="Markdown" > /dev/null
|
|
fi
|
|
}
|
|
|
|
run_backup() {
|
|
local desc="$1"; shift
|
|
log "Backing up $desc..."
|
|
if "$@"; then
|
|
log "$desc OK"
|
|
else
|
|
log "FAILED: $desc"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
}
|
|
|
|
log "=== Backup started ==="
|
|
|
|
# Database dumps
|
|
run_backup "Gitea DB" docker exec gitea-db pg_dump -U gitea -f /tmp/gitea_dump.sql gitea
|
|
docker cp gitea-db:/tmp/gitea_dump.sql "$BACKUP_DIR/gitea_db_$DATE.sql" 2>/dev/null
|
|
|
|
run_backup "OPC DB" docker exec gitea-db pg_dump -U gitea -f /tmp/opc_dump.sql opc
|
|
docker cp gitea-db:/tmp/opc_dump.sql "$BACKUP_DIR/opc_db_$DATE.sql" 2>/dev/null
|
|
|
|
run_backup "Immich DB" docker exec immich-db pg_dump -U postgres -f /tmp/immich_dump.sql immich
|
|
docker cp immich-db:/tmp/immich_dump.sql "$BACKUP_DIR/immich_db_$DATE.sql" 2>/dev/null
|
|
|
|
# Config backups
|
|
CONFIGS=()
|
|
for p in \
|
|
/volume1/docker/nas-dashboard/.env \
|
|
/volume1/docker/nas-dashboard/auth.json \
|
|
/volume1/docker/nas-dashboard/rbac.json \
|
|
/volume1/docker/nas-dashboard/audit.log \
|
|
/volume1/docker/gitea/conf \
|
|
/volume1/docker/immich/.env \
|
|
/volume1/docker/navidrome/data/navidrome.db \
|
|
/volume1/docker/n8n/docker-compose.yml; do
|
|
[ -e "$p" ] && CONFIGS+=("$p")
|
|
done
|
|
|
|
if [ ${#CONFIGS[@]} -gt 0 ]; then
|
|
run_backup "Configs" tar -czf "$BACKUP_DIR/configs_$DATE.tar.gz" "${CONFIGS[@]}"
|
|
fi
|
|
|
|
# Cleanup old backups
|
|
log "Cleaning backups older than $RETENTION_DAYS days..."
|
|
find "$BACKUP_DIR" -type f \( -name "*.sql" -o -name "*.tar.gz" \) -mtime +$RETENTION_DAYS -delete
|
|
|
|
# Summary
|
|
log "=== Backup finished ($ERRORS errors) ==="
|
|
|
|
# Off-site cloud sync
|
|
if [ "$CLOUD_ENABLED" = "true" ] && type cloud_backup &>/dev/null; then
|
|
cloud_backup || ERRORS=$((ERRORS + CLOUD_ERRORS))
|
|
fi
|
|
|
|
if [ $ERRORS -gt 0 ]; then
|
|
notify "🔴 *NAS Backup FAILED* ($DATE)
|
|
$ERRORS error(s) — check logs"
|
|
else
|
|
notify "🟢 *NAS Backup OK* ($DATE)"
|
|
fi
|