#!/bin/bash set -euo pipefail ############################ # Konfiguration ############################ SFTP_SERVER="sftp.domain.tld" SFTP_PORT="22" SFTP_USER="sftp-user" SFTP_PASS="passw0rd" REMOTE_DIR="/uploads" EMAIL="" ############################ # Basis ############################ HOSTNAME=$(hostname) DATE=$(date "+%Y-%m-%d_%H-%M") BACKUP_DIR="/backups/docker" LOG_DIR="/var/log/script-logs" mkdir -p "$BACKUP_DIR" "$LOG_DIR" PROJECT_NAME="" ARCHIVE_FILE="" LOG_FILE="" ############################ # Mail Funktion ############################ send_mail() { STATUS="$1" SUBJECT="[$HOSTNAME] Docker Backup $STATUS ($PROJECT_NAME)" if command -v mail >/dev/null 2>&1; then mail -s "$SUBJECT" "$EMAIL" -A "$LOG_FILE" < ${#PROJECTS[@]} )); then echo "Ungültige Auswahl." exit 1 fi PROJECT_NAME="${PROJECTS[$((SELECTION-1))]}" LOG_FILE="$LOG_DIR/docker-backup-${PROJECT_NAME}.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo echo "===== Backup gestartet =====" echo "Projekt : $PROJECT_NAME" echo "Host : $HOSTNAME" echo "Datum : $DATE" echo ############################ # Container sammeln ############################ mapfile -t CONTAINERS < <( docker ps \ --filter "label=com.docker.compose.project=$PROJECT_NAME" \ --format '{{.Names}}' ) echo "Container im Stack:" printf ' - %s\n' "${CONTAINERS[@]}" echo ############################ # Mounts sammeln ############################ MOUNTS=() for c in "${CONTAINERS[@]}"; do mapfile -t PATHS < <( docker inspect "$c" \ | jq -r '.[0].Mounts[] | select(.Type=="volume" or .Type=="bind") | .Source' ) for p in "${PATHS[@]}"; do [[ -d "$p" ]] && MOUNTS+=("$p") done done mapfile -t MOUNTS < <(printf "%s\n" "${MOUNTS[@]}" | sort -u) if [[ ${#MOUNTS[@]} -eq 0 ]]; then echo "Keine gültigen Mounts gefunden." exit 1 fi ############################ # Stack stoppen ############################ echo "Stoppe Stack..." docker stop "${CONTAINERS[@]}" >/dev/null sleep 3 ############################ # Daten kopieren ############################ TMP_DIR="/tmp/docker-backup-$PROJECT_NAME" rm -rf "$TMP_DIR" mkdir -p "$TMP_DIR" echo "Kopiere Daten..." for mount in "${MOUNTS[@]}"; do BASENAME=$(basename "$mount") rsync -a "$mount/" "$TMP_DIR/$BASENAME/" done ############################ # ZIP Archiv erstellen ############################ ARCHIVE_FILE="$BACKUP_DIR/${HOSTNAME}-${PROJECT_NAME}-${DATE}.zip" echo "Erstelle ZIP Archiv..." TOTAL_SIZE=$(du -sb "$TMP_DIR" | awk '{print $1}') ( cd "$TMP_DIR" zip -r -q - . ) | pv -s "$TOTAL_SIZE" > "$ARCHIVE_FILE" echo "Archiv erstellt: $ARCHIVE_FILE" ############################ # Stack starten ############################ echo "Starte Stack..." docker start "${CONTAINERS[@]}" >/dev/null echo "Stack läuft wieder." ############################ # Optionaler Upload ############################ echo read -p "Backup zum SFTP hochladen? (j/n): " DO_UPLOAD if [[ "$DO_UPLOAD" == "j" ]]; then if [[ -z "$SFTP_PASS" ]]; then read -s -p "SFTP Passwort eingeben: " SFTP_PASS echo fi echo "Starte Upload..." sshpass -p "$SFTP_PASS" \ scp -P "$SFTP_PORT" "$ARCHIVE_FILE" \ "$SFTP_USER@$SFTP_SERVER:$REMOTE_DIR/" echo "Upload abgeschlossen." fi ############################ # Cleanup ############################ rm -rf "$TMP_DIR" echo "Backup erfolgreich abgeschlossen." send_mail "ERFOLGREICH" echo "===== Fertig =====" exit 0