#!/bin/bash # ============================================ # Backup Monitor - Prueft Backup-Alter # Warnt wenn Backups zu alt oder fehlend # ============================================ # Aufruf: bash backup-monitor.sh # Cron: 0 8 * * * bash /opt/scripts/backup-monitor.sh # Quelle: sgit.space/downloads # ============================================ set -euo pipefail # --- Konfiguration --- # Maximales Alter in Stunden bevor Warnung MAX_AGE_VZDUMP=26 # vzdump: 1x taeglich = max 26h MAX_AGE_APP=50 # App-Backups: 1x taeglich = max 50h # Backup-Verzeichnisse pruefen (anpassen!) VZDUMP_DIR="/mnt/backup/dump" # vzdump Storage APP_BACKUP_DIRS=( "/mnt/backup/app-backups/mail" "/mnt/backup/app-backups/n8n" "/mnt/backup/app-backups/bitwarden" ) # Telegram (optional) TELEGRAM_ENABLED=false # TELEGRAM_BOT_TOKEN="your-token" # TELEGRAM_CHAT_ID="your-chat-id" # --- Farben --- GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' NC='\033[0m' WARNINGS=0 OK=0 ERRORS="" log() { echo -e "$1"; } check_backup_age() { local DIR="$1" local MAX_HOURS="$2" local LABEL="$3" if [ ! -d "$DIR" ]; then log "${RED}FEHLER${NC}: $LABEL — Verzeichnis $DIR nicht gefunden" WARNINGS=$((WARNINGS + 1)) ERRORS="${ERRORS}\n$LABEL: Verzeichnis fehlt" return fi # Neueste Datei finden NEWEST=$(find "$DIR" -type f -name "*.tar" -o -name "*.gz" -o -name "*.zst" -o -name "*.lzo" -o -name "*.sql" -o -name "*.dump" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -1) if [ -z "$NEWEST" ]; then log "${RED}FEHLER${NC}: $LABEL — Keine Backup-Dateien gefunden" WARNINGS=$((WARNINGS + 1)) ERRORS="${ERRORS}\n$LABEL: Keine Dateien" return fi # Alter berechnen FILE_AGE_SEC=$(( $(date +%s) - $(stat -c %Y "$NEWEST") )) FILE_AGE_HOURS=$(( FILE_AGE_SEC / 3600 )) FILE_SIZE=$(du -sh "$NEWEST" | cut -f1) FILE_NAME=$(basename "$NEWEST") if [ "$FILE_AGE_HOURS" -gt "$MAX_HOURS" ]; then log "${RED}WARNUNG${NC}: $LABEL — ${FILE_AGE_HOURS}h alt (max: ${MAX_HOURS}h)" log " Datei: $FILE_NAME ($FILE_SIZE)" WARNINGS=$((WARNINGS + 1)) ERRORS="${ERRORS}\n$LABEL: ${FILE_AGE_HOURS}h alt" else log "${GREEN}OK${NC}: $LABEL — ${FILE_AGE_HOURS}h alt ($FILE_SIZE)" OK=$((OK + 1)) fi } # === vzdump Backups === log "\n===== Backup Monitor =====" log "Datum: $(date '+%Y-%m-%d %H:%M')\n" log "--- vzdump Backups ---" if [ -d "$VZDUMP_DIR" ]; then # Pro Container das neueste Backup pruefen for CONF in $(find "$VZDUMP_DIR" -name "vzdump-lxc-*.tar.*" -o -name "vzdump-lxc-*.vma.*" 2>/dev/null | sed 's/.*vzdump-lxc-\([0-9]*\).*/\1/' | sort -un); do CT_DIR="$VZDUMP_DIR" LABEL="vzdump CT $CONF" NEWEST=$(find "$CT_DIR" -name "vzdump-lxc-${CONF}-*" -type f 2>/dev/null | xargs -r ls -t 2>/dev/null | head -1) if [ -n "$NEWEST" ]; then FILE_AGE_SEC=$(( $(date +%s) - $(stat -c %Y "$NEWEST") )) FILE_AGE_HOURS=$(( FILE_AGE_SEC / 3600 )) FILE_SIZE=$(du -sh "$NEWEST" | cut -f1) if [ "$FILE_AGE_HOURS" -gt "$MAX_AGE_VZDUMP" ]; then log "${RED}WARNUNG${NC}: $LABEL — ${FILE_AGE_HOURS}h alt" WARNINGS=$((WARNINGS + 1)) ERRORS="${ERRORS}\n$LABEL: ${FILE_AGE_HOURS}h alt" else log "${GREEN}OK${NC}: $LABEL — ${FILE_AGE_HOURS}h ($FILE_SIZE)" OK=$((OK + 1)) fi fi done else log "${YELLOW}SKIP${NC}: vzdump-Verzeichnis nicht gefunden ($VZDUMP_DIR)" fi # === App-Level Backups === log "\n--- App-Level Backups ---" for DIR in "${APP_BACKUP_DIRS[@]}"; do LABEL=$(basename "$DIR") check_backup_age "$DIR" "$MAX_AGE_APP" "$LABEL" done # === Zusammenfassung === log "\n===== Ergebnis =====" log "OK: $OK | Warnungen: $WARNINGS" if [ "$WARNINGS" -gt 0 ]; then log "${RED}ACHTUNG: $WARNINGS Backup(s) pruefen!${NC}" if [ "$TELEGRAM_ENABLED" = true ]; then MSG="Backup Monitor OK: $OK | Warnungen: $WARNINGS $(echo -e "$ERRORS")" curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ -d chat_id="${TELEGRAM_CHAT_ID}" -d text="$MSG" -d parse_mode="HTML" > /dev/null 2>&1 || true fi exit 1 fi exit 0