Update: Rsync-Tar Backup   Vor kurzem aktualisiert!


In dem Beitrag automatisierte Server-Sicherung mit Rsync, Tar und HTML-Reports ging es um ein Bash-Skript, das Backups per Rsync erstellt und Berichte per HTML-Mail versendet. Dazu gibt es jetzt ein paar Verbesserungen.

Die Verbesserungen im Detail

Das „Vanished Files“-Phänomen (Exit Code 24)

Der E-Mail-Report zeigte ein rotes „Fehlgeschlagen“ beim Rsync-Abgleich an, aber die Daten scheinen alle da zu sein. Ein Blick ins Log verrät:

rsync warning: some files vanished before they could be transferred (code 24)

Das passiert bei laufenden Systemen ständig. Wenn eine temporäre Datei oder ein Cache (z. B. von Matomo oder WordPress) gelöscht wird, während Rsync gerade die Dateiliste abarbeitet, bricht Rsync nicht ab, gibt aber die Warnung 24 aus. Das ist kein kritischer Fehler. Ich habe das Skript so angepasst, dass Exit Code 24 als „OK (mit Warnung)“ gewertet wird.

Ein besserer E-Mail-Betreff

Bisher hat das Skript im Betreff der Mail nur den Status der Archivierung ($TAR_STATUS) ausgegeben. Das Problem: Wenn der Rsync-Abgleich komplett fehlschlug, die (dann leere) Archivierung aber technisch „erfolgreich“ durchlief, stand im Betreff trotzdem „Status: OK“.

Daher gibt es jetzt eine neue Variable GLOBAL_STATUS. Der Betreff ist jetzt nur noch dann „OK“, wenn beide Schritte sauber durchgelaufen sind.

3. Encoding-Fix (Umlaute in HTML)

In manchen Mail-Clients wurden Umlaute wie in „Größe“ falsch dargestellt. Daher wurde der HTML-Teil auf HTML-Entities umgestellt (ö), damit der Report in jedem Postfach sauber aussieht.


Das aktualisierte Skript

Hier ist die anonymisierte Vorlage zum Kopieren:

#!/bin/bash
# ==============================================================================
# Remote-Backup-Skript v2.0 - Jetzt mit Exit-Code-Handling & Global-Status
# ==============================================================================

# --- 1. KONFIGURATION ---
NAME="${1:-meinserver}"                # Kurzname für Logs und Dateien
REMOTE_HOST="${2:-remote.example.com}" # Hostname oder IP des Zielservers
SSH_PORT="22"                          # SSH-Port (Standard: 22)
MNT="/mnt/backup-storage"              # Lokaler Pfad zum Backup-Speicher
DIR="$MNT/$REMOTE_HOST"
SENDER="backup@deine-domain.de"        # Absender der Status-Mail
RECEIVER="admin@deine-domain.de"       # Empfänger der Status-Mail
RSYNC_LOG="$MNT/$NAME-rsync.log"
TMP_MAIL=$(mktemp /tmp/backup_mail.XXXXXX.html)

# Ausschlüsse für das Backup
EXCLUDES=(--exclude /proc/ --exclude /sys/ --exclude /dev/ --exclude /tmp/ --exclude /run/ --exclude /mnt/ --exclude /media/ --exclude /var/cache/)

DAY=$(date +%w) # 0=Sonntag (Full), 1-6=Diff
[ -n "$3" ] && DAY=$3

# --- 2. HILFSFUNKTIONEN ---
log_event() {
    echo "$(date '+%d.%m.%Y %H:%M:%S') : $1" >> "$MNT/backup.log"
    /usr/bin/logger -t "Backup-$NAME" "$1"
}

get_size() {
    [ -f "$1" ] && du -sh "$1" | cut -f1 || echo "0"
}

# --- 3. RSYNC SYNCHRONISATION ---
log_event "Start: $NAME (Level $DAY) auf $REMOTE_HOST"

rsync -avzHPWAX --delete-after --bwlimit=1000 "${EXCLUDES[@]}" \
      -e "ssh -p $SSH_PORT" root@"$REMOTE_HOST":/ "$DIR/" > "$RSYNC_LOG" 2>&1

RSYNC_RC=$?

# Fehlerbehandlung: Code 24 (Verschwundene Dateien) ist unkritisch
if [ $RSYNC_RC -eq 0 ] || [ $RSYNC_RC -eq 24 ]; then
    RSYNC_STATUS="OK"
    [ $RSYNC_RC -eq 24 ] && RSYNC_STATUS="OK (Warnung: Dateien verschwunden)"
    COLOR_RSYNC="#5cb85c" # Grün
    touch "$DIR/.stamp"
else
    RSYNC_STATUS="FEHLER (Code $RSYNC_RC)"
    COLOR_RSYNC="#d9534f" # Rot
    log_event "KRITISCH: Rsync-Fehler bei $NAME"
fi

# --- 4. TAR ARCHIVIERUNG ---
cd "$DIR" || exit
ARCHIVE="$MNT/backup-$NAME-$DAY.tgz"
LAST_FULL="$MNT/backup-$NAME-0.tgz"

if [ "$DAY" = "0" ]; then
    [ -f "$ARCHIVE" ] && mv "$ARCHIVE" "$MNT/backup-$NAME-lastweek.tgz"
    tar cvfz "$ARCHIVE" ./ > /dev/null 2>&1
else
    if [ -f "$LAST_FULL" ]; then
        find ./ -cnewer "$LAST_FULL" -type f > "$MNT/$NAME-tar-list.log"
        tar cvfz "$ARCHIVE" -T "$MNT/$NAME-tar-list.log" > /dev/null 2>&1
    else
        tar cvfz "$ARCHIVE" ./ > /dev/null 2>&1
    fi
fi

if [ -s "$ARCHIVE" ]; then
    TAR_STATUS="OK"
    COLOR_TAR="#5cb85c"
else
    TAR_STATUS="FEHLER"
    COLOR_TAR="#d9534f"
fi

# --- 5. GLOBALER STATUS FÜR DEN BETREFF ---
GLOBAL_STATUS="OK"
if [ "$RSYNC_STATUS" != "OK" ] || [ "$TAR_STATUS" != "OK" ]; then
    # Wenn Rsync nur Warnung 24 hatte und Tar OK ist -> Warnung statt Fehler
    if [ $RSYNC_RC -eq 24 ] && [ "$TAR_STATUS" == "OK" ]; then
        GLOBAL_STATUS="WARNUNG"
    else
        GLOBAL_STATUS="FEHLER"
    fi
fi

# --- 6. HTML BERICHT ERSTELLEN ---
cat <<EOF > "$TMP_MAIL"
<html>
<head>
<style>
    body { font-family: Arial, sans-serif; background: #f4f4f4; padding: 20px; }
    .container { background: #fff; padding: 20px; border-radius: 8px; border: 1px solid #ddd; }
    .status-box { padding: 8px; color: white; font-weight: bold; border-radius: 4px; display: inline-block; }
    table { width: 100%; margin-top: 20px; border-collapse: collapse; }
    th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
    pre { background: #333; color: #adff2f; padding: 15px; border-radius: 4px; font-size: 11px; overflow-x: auto; }
</style>
</head>
<body>
<div class="container">
    <h2>Backup-Bericht: $NAME</h2>
    <p><strong>Datum:</strong> $(date "+%d.%m.%Y %H:%M") | <strong>Level:</strong> $DAY</p>
    <table>
        <tr><th>Prozess</th><th>Status</th></tr>
        <tr><td>Rsync Abgleich</td><td><span class="status-box" style="background:$COLOR_RSYNC;">$RSYNC_STATUS</span></td></tr>
        <tr><td>Tar Archivierung</td><td><span class="status-box" style="background:$COLOR_TAR;">$TAR_STATUS</span></td></tr>
    </table>
    <h3>Archiv-Details</h3>
    <ul>
        <li>Datei: <code>$(basename "$ARCHIVE")</code></li>
        <li>Gr&ouml;&szlig;e: $(get_size "$ARCHIVE")</li>
    </ul>
    <h3>Rsync Log (Letzte Zeilen)</h3>
    <pre>$(tail -n 8 "$RSYNC_LOG")</pre>
</div>
</body>
</html>
EOF

# --- 7. VERSAND ---
(
  echo "From: $SENDER"
  echo "To: $RECEIVER"
  echo "Subject: [Backup] $NAME - $GLOBAL_STATUS (R:$RSYNC_STATUS / T:$TAR_STATUS)"
  echo "Content-Type: text/html; charset=UTF-8"
  echo "MIME-Version: 1.0"
  echo ""
  cat "$TMP_MAIL"
) | /usr/sbin/sendmail -t

log_event "Backup $NAME beendet. Status: $GLOBAL_STATUS"
rm "$TMP_MAIL"

Solche Skripte sind irgendwie nie „fertig“. Erst wenn man sieht, wie es auf flüchtige Dateien oder unerwartete Abbrüche reagiert, kann man verfeinern. Mit der neuen Version klappt es jetzt schon besser.