#!/usr/bin/env bash set -euo pipefail REPO="/backup/borgrepo" MOUNT_BASE="/tmp/borg-sshfs" export BORG_PASSPHRASE BORG_PASSPHRASE="$(cat /app/host_vars/repokey)" HOSTS_FILE="$(dirname "$0")/host_vars/hosts" TIMESTAMP="$(date +%Y-%m-%dT%H%M%S)" HOSTS=() while read -r host; do [[ -z "$host" || "$host" =~ ^# ]] && continue HOSTS+=("$host") done < "$HOSTS_FILE" echo "Backing up hosts:" printf ' - %s\n' "${HOSTS[@]}" FAILED_HOSTS=() for HOST in "${HOSTS[@]}"; do ARCHIVE="${HOST}-backup-${TIMESTAMP}" MOUNT_DIR="${MOUNT_BASE}/${HOST}" echo "--- Backing up $HOST ---" mkdir -p "$MOUNT_DIR" # Pull mode: mount client's /BACKUP locally via SSHFS if ! sshfs "${HOST}:/BACKUP" "$MOUNT_DIR" \ -o BatchMode=yes,reconnect,follow_symlinks,ServerAliveInterval=15,ServerAliveCountMax=3; then echo "WARNING: Failed to mount $HOST:/BACKUP via SSHFS" FAILED_HOSTS+=("$HOST") rmdir "$MOUNT_DIR" continue fi # SSHFS (FUSE/SFTP) does not guarantee stable inode numbers, # so instruct borg to use ctime+size for file change detection instead. if ! borg create \ "$REPO::$ARCHIVE" \ "$MOUNT_DIR" \ --pattern "+ sh:**/current*" \ --pattern "- *" \ --files-cache ctime,size \ --stats \ --compression lz4; then echo "WARNING: Failed to back up $HOST" FAILED_HOSTS+=("$HOST") fi # Unmount and clean up regardless of borg exit status umount "$MOUNT_DIR" rmdir "$MOUNT_DIR" done rmdir "$MOUNT_BASE" 2>/dev/null || true echo "Applying retention policy..." for HOST in "${HOSTS[@]}"; do borg prune \ "$REPO" \ --keep-daily=7 \ --keep-weekly=8 \ --glob-archives "${HOST}-backup-*" \ --stats done if [[ ${#FAILED_HOSTS[@]} -gt 0 ]]; then echo "DONE WITH ERRORS:" printf ' - %s\n' "${FAILED_HOSTS[@]}" exit 1 fi echo "Done: all hosts backed up at $TIMESTAMP"