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öß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.