diff --git a/borg-pull-backup.sh b/borg-pull-backup.sh new file mode 100644 index 0000000..53663e0 --- /dev/null +++ b/borg-pull-backup.sh @@ -0,0 +1,180 @@ +#!/bin/bash +# ============================================================================= +# BorgBackup Pull-Script – läuft auf dem Raspberry Pi +# Zieht Daten vom Docker-Host per sshfs und sichert sie lokal +# ============================================================================= +set -euo pipefail + +# ============================================================================= +# KONFIGURATION – hier anpassen +# ============================================================================= + +REMOTE_HOST="docker-host" # IP oder Hostname des Docker-Hosts +REMOTE_USER="borgbackup" # SSH-User auf dem Docker-Host +SSH_KEY="/home/borg/.ssh/borg_pull" # SSH-Key des borg-Users auf dem Pi +MOUNT_POINT="/mnt/borg-pull/${REMOTE_HOST}" # Temporärer sshfs-Mountpunkt + +BORG_REPO="/media/backup/borg/${REMOTE_HOST}" # Lokales Borg-Repository auf dem Pi +export BORG_PASSPHRASE="HIER-DEIN-PASSWORT" # Borg-Verschlüsselungspasswort + +# Was vom Docker-Host gesichert werden soll (Pfade auf dem Remote-Host) +BACKUP_PATHS=( + "etc" + "home" + "root" + "opt" + "var/lib/docker/volumes" +) + +# Ausschlüsse (relativ zum Mountpunkt) +EXCLUDES=( + "--exclude" "${MOUNT_POINT}/var/lib/docker/volumes/*/tmp" + "--exclude" "${MOUNT_POINT}/proc" + "--exclude" "${MOUNT_POINT}/sys" + "--exclude" "${MOUNT_POINT}/dev" + "--exclude" "${MOUNT_POINT}/run" + "--exclude" "${MOUNT_POINT}/tmp" + "--exclude" "${MOUNT_POINT}/var/cache" + "--exclude" "*.pyc" + "--exclude" "*.log" +) + +# Aufbewahrungsrichtlinie +KEEP_DAILY=7 +KEEP_WEEKLY=4 +KEEP_MONTHLY=6 + +LOG="/var/log/borg-pull-backup.log" + +# ============================================================================= +# FUNKTIONEN +# ============================================================================= + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG" +} + +error_exit() { + log "FEHLER: $*" + cleanup + exit 1 +} + +cleanup() { + log "Aufräumen..." + if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then + log "Unmounte ${MOUNT_POINT}..." + fusermount -u "$MOUNT_POINT" || umount "$MOUNT_POINT" || true + fi +} + +# Cleanup auch bei unerwarteten Fehlern +trap cleanup EXIT ERR + +# ============================================================================= +# HAUPTPROGRAMM +# ============================================================================= + +log "============================================" +log "Starte Pull-Backup von ${REMOTE_USER}@${REMOTE_HOST}" +log "============================================" + +# --- 1. Voraussetzungen prüfen --- +for cmd in borg sshfs fusermount ssh; do + command -v "$cmd" &>/dev/null || error_exit "Programm nicht gefunden: $cmd" +done + +[ -f "$SSH_KEY" ] || error_exit "SSH-Key nicht gefunden: $SSH_KEY" +[ -d "$BORG_REPO" ] || error_exit "Borg-Repo nicht gefunden: $BORG_REPO – erst initialisieren!" + +# --- 2. Mountpunkt vorbereiten --- +mkdir -p "$MOUNT_POINT" + +if mountpoint -q "$MOUNT_POINT"; then + log "Vorheriger Mount gefunden, unmounte zuerst..." + fusermount -u "$MOUNT_POINT" || true + sleep 2 +fi + +# --- 3. Docker-Host mounten (read-only!) --- +log "Mounte ${REMOTE_HOST} nach ${MOUNT_POINT} (read-only)..." +sshfs \ + -o ro \ + -o allow_other \ + -o StrictHostKeyChecking=no \ + -o IdentityFile="${SSH_KEY}" \ + -o ServerAliveInterval=30 \ + -o ServerAliveCountMax=3 \ + -o reconnect \ + "${REMOTE_USER}@${REMOTE_HOST}:/" \ + "$MOUNT_POINT" \ + || error_exit "sshfs-Mount fehlgeschlagen" + +log "Mount erfolgreich." + +# --- 4. Backup-Pfade zusammenbauen --- +FULL_PATHS=() +for p in "${BACKUP_PATHS[@]}"; do + full="${MOUNT_POINT}/${p}" + if [ -d "$full" ]; then + FULL_PATHS+=("$full") + log " Pfad gefunden: $full" + else + log " WARNUNG: Pfad nicht gefunden, wird übersprungen: $full" + fi +done + +[ ${#FULL_PATHS[@]} -gt 0 ] || error_exit "Keine gültigen Backup-Pfade gefunden!" + +# --- 5. Borg-Backup erstellen --- +ARCHIVE_NAME="${REMOTE_HOST}-$(date '+%Y-%m-%dT%H-%M-%S')" +log "Erstelle Archiv: ${ARCHIVE_NAME}" + +borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --compression lz4 \ + --exclude-caches \ + "${EXCLUDES[@]}" \ + "${BORG_REPO}::${ARCHIVE_NAME}" \ + "${FULL_PATHS[@]}" \ + 2>&1 | tee -a "$LOG" + +BORG_EXIT=${PIPESTATUS[0]} + +case $BORG_EXIT in + 0) log "Backup erfolgreich abgeschlossen." ;; + 1) log "WARNUNG: Backup mit Warnungen abgeschlossen (Exit 1)." ;; + *) error_exit "Backup fehlgeschlagen (Exit ${BORG_EXIT})." ;; +esac + +# --- 6. Unmounten --- +log "Unmounte ${MOUNT_POINT}..." +fusermount -u "$MOUNT_POINT" +log "Unmount erfolgreich." + +# Trap zurücksetzen, da wir selbst gemountet haben +trap - EXIT ERR + +# --- 7. Alte Archive bereinigen --- +log "Bereinige alte Archive (daily=${KEEP_DAILY}, weekly=${KEEP_WEEKLY}, monthly=${KEEP_MONTHLY})..." + +borg prune \ + --list \ + --glob-archives "${REMOTE_HOST}-*" \ + --keep-daily "$KEEP_DAILY" \ + --keep-weekly "$KEEP_WEEKLY" \ + --keep-monthly "$KEEP_MONTHLY" \ + "$BORG_REPO" \ + 2>&1 | tee -a "$LOG" + +# --- 8. Repo komprimieren --- +log "Komprimiere Repository..." +borg compact "$BORG_REPO" 2>&1 | tee -a "$LOG" + +log "============================================" +log "Pull-Backup abgeschlossen: ${ARCHIVE_NAME}" +log "============================================" \ No newline at end of file