Files
datensicherung_rsync/README.md
T
2026-04-28 10:42:31 +02:00

204 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# README Snapshot-Backup mit rsync
Dieses Skript erstellt Snapshots des Home-Verzeichnisses mit `rsync` und `--link-dest`.[cite:43][cite:34] Jeder Snapshot sieht wie ein vollständiges Backup aus, aber unveränderte Dateien werden per Hardlink wiederverwendet, sodass Speicherplatz gespart wird.[cite:34][cite:92]
## Zweck
Das Skript sichert den Inhalt von `HOME` auf ein externes Zielverzeichnis und legt dabei zeitgestempelte Snapshot-Ordner an.[cite:43] Über den Symlink `latest` wird immer auf den zuletzt erfolgreichen Sicherungsstand verwiesen.[cite:34][cite:92]
Typische Vorteile dieses Ansatzes:
- Einfach browsebare Backups, weil jeder Snapshot als normales Verzeichnis vorliegt.[cite:43][cite:91]
- Platzersparnis durch Hardlinks mit `--link-dest`.[cite:34][cite:92]
- Aufbewahrung mehrerer Sicherungsstände durch Rotation älterer Snapshots.[cite:43]
## Voraussetzungen
Für `--link-dest` sollte das Ziel auf einem Linux-Dateisystem liegen, das Hardlinks sauber unterstützt, etwa ext4, XFS oder btrfs.[cite:34][cite:91] Dateisysteme wie exFAT sind für diese Snapshot-Technik ungeeignet.[cite:34]
Benötigte Werkzeuge:
- `bash`
- `rsync`
- `readlink`
- `tee`
- Standard-Tools wie `mkdir`, `ln`, `ls`, `tail`, `rm`
## Verzeichnisaufbau
Das Skript erzeugt pro Lauf einen neuen Snapshot mit Zeitstempel.[cite:43][cite:92]
Beispiel:
```text
/run/media/hans/usbsicherung/jacboy_sicherung/
└── <hostname>/
├── 2026-04-28_10-30-00/
├── 2026-04-29_10-30-00/
├── latest -> /run/media/hans/usbsicherung/jacboy_sicherung/<hostname>/2026-04-29_10-30-00
└── logs/
```
## Funktionsweise
Das Skript prüft zunächst, ob das Zielverzeichnis existiert und beschreibbar ist, und legt anschließend Snapshot- und Log-Verzeichnisse an.[cite:43][cite:97] Danach wird `rsync` mit Archivoptionen sowie Ausschlüssen für nicht benötigte Verzeichnisse ausgeführt.[cite:43][cite:91]
Wenn bereits ein gültiger vorheriger Snapshot existiert, wird dieser über `--link-dest` als Referenz verwendet.[cite:34][cite:92] Unveränderte Dateien werden dann nicht erneut kopiert, sondern als Hardlinks im neuen Snapshot eingebunden.[cite:34]
Nach erfolgreichem Lauf aktualisiert das Skript den Symlink `latest` und entfernt ältere Snapshots oberhalb des konfigurierten Aufbewahrungswerts `KEEP`.[cite:43]
## Verwendete rsync-Optionen
| Option | Bedeutung |
|---|---|
| `-a` | Archivmodus: rekursiv kopieren und Metadaten erhalten.[cite:91][cite:43] |
| `-H` | Hardlinks in der Quelle erhalten.[cite:91] |
| `-A` | ACLs mitnehmen, sofern vorhanden.[cite:91] |
| `-X` | Erweiterte Attribute mitnehmen.[cite:91] |
| `-x` | Auf demselben Dateisystem bleiben; zusätzliche Mounts unterhalb von `HOME` werden nicht traversiert.[cite:91][cite:43] |
| `--delete` | Entfernt Dateien im neuen Snapshot, die in der Quelle nicht mehr vorhanden sind; ältere Snapshots bleiben dabei unberührt.[cite:91][cite:34] |
| `--delete-excluded` | Entfernt auch explizit ausgeschlossene Inhalte aus dem Ziel-Snapshot.[cite:91] |
| `--link-dest` | Verweist auf den letzten Snapshot, damit unveränderte Dateien per Hardlink wiederverwendet werden.[cite:34][cite:92] |
## Anpassbare Variablen
Die wichtigsten Variablen stehen am Anfang des Skripts:
```bash
SRC="${HOME}/"
DEST_BASE="/run/media/hans/usbsicherung/jacboy_sicherung"
KEEP=30
```
- `SRC`: Quellverzeichnis, standardmäßig das eigene Home-Verzeichnis.
- `DEST_BASE`: Basisverzeichnis auf dem Backup-Medium.
- `KEEP`: Anzahl der Snapshots, die aufbewahrt werden sollen.
Auch die Ausschlussliste kann direkt im Array `EXCLUDES` angepasst werden, zum Beispiel für Cache-, Download- oder temporäre Verzeichnisse.[cite:43]
## Verwendung
Syntax prüfen:
```bash
bash -n sicherung.sh
```
Skript ausführen:
```bash
bash sicherung.sh
```
Für einen sicheren Test kann vorübergehend `--dry-run` in die `RSYNC_OPTS` aufgenommen werden; dann zeigt `rsync` nur an, was passieren würde, ohne tatsächlich Daten zu schreiben.[cite:91][cite:43]
## Beispielskript
```bash
#!/usr/bin/env bash
set -Eeuo pipefail
SRC="${HOME}/"
DEST_BASE="/run/media/hans/usbsicherung/jacboy_sicherung"
HOST="$(hostname -s)"
STAMP="$(date +%F_%H-%M-%S)"
SNAPSHOT_DIR="${DEST_BASE}/${HOST}/${STAMP}"
LAST_LINK="${DEST_BASE}/${HOST}/latest"
LOG_DIR="${DEST_BASE}/${HOST}/logs"
LOG_FILE="${LOG_DIR}/backup-${STAMP}.log"
KEEP=30
log() {
local msg="[$(date +%F\ %T)] $*"
echo "${msg}"
if [[ -n "${LOG_FILE:-}" ]]; then
mkdir -p "$(dirname "${LOG_FILE}")"
echo "${msg}" >> "${LOG_FILE}"
fi
}
fail() {
log "FEHLER: $*"
exit 1
}
if [[ ! -d "${DEST_BASE}" ]]; then
fail "Zielbasis ${DEST_BASE} existiert nicht oder ist nicht gemountet. Bitte USB-Laufwerk prüfen."
fi
if [[ ! -w "${DEST_BASE}" ]]; then
fail "Zielbasis ${DEST_BASE} ist nicht beschreibbar. Rechte/Mount prüfen."
fi
mkdir -p "${SNAPSHOT_DIR}" "${LOG_DIR}"
log "Starte Backup: ${SRC} -> ${SNAPSHOT_DIR}"
RSYNC_OPTS=(
-aHAXx
--numeric-ids
--delete
--delete-excluded
--info=stats2,progress2
--human-readable
--partial
)
EXCLUDES=(
--exclude=".cache/"
--exclude="Downloads/"
--exclude=".local/share/Trash/"
--exclude=".gvfs/"
--exclude=".dotnet/"
--exclude=".codegpt/"
--exclude=".copilot/"
--exclude=".var/"
--exclude=".vscode/"
--exclude="temp/"
)
if [[ -L "${LAST_LINK}" ]] && [[ -d "$(readlink -f "${LAST_LINK}")" ]]; then
RSYNC_OPTS+=(--link-dest="$(readlink -f "${LAST_LINK}")")
log "Verwende link-dest: $(readlink -f "${LAST_LINK}")"
else
log "Kein gültiger letzter Snapshot gefunden es wird ein vollständiger Lauf erstellt."
fi
if rsync "${RSYNC_OPTS[@]}" \
"${EXCLUDES[@]}" \
"${SRC}" "${SNAPSHOT_DIR}/" | tee -a "${LOG_FILE}"
then
log "rsync erfolgreich abgeschlossen."
else
fail "rsync ist mit einem Fehler beendet worden."
fi
ln -sfn "${SNAPSHOT_DIR}" "${LAST_LINK}"
log "latest-Link zeigt jetzt auf ${SNAPSHOT_DIR}"
cd "${DEST_BASE}/${HOST}"
SNAPS=(20*/)
if (( ${#SNAPS[@]} > KEEP )); then
log "Rotationslauf: Es existieren ${#SNAPS[@]} Snapshots, KEEP=${KEEP}."
ls -1dt 20*/ | tail -n +$((KEEP + 1)) | while read -r old; do
log "Lösche alten Snapshot: ${old}"
rm -rf -- "${old}"
done
else
log "Rotationslauf: Es müssen keine alten Snapshots gelöscht werden (Anzahl: ${#SNAPS[@]}, KEEP=${KEEP})."
fi
log "Backup abgeschlossen."
```
## Sicherheitshinweise
Ein erster Testlauf sollte als Dry-Run erfolgen, um Ausschlüsse, Zielpfad und Verhalten zu prüfen, bevor echte Daten geschrieben oder gelöscht werden.[cite:91][cite:43] Das Zielverzeichnis sollte nicht innerhalb des zu sichernden Home-Verzeichnisses liegen, um Rekursionen und ungewollte Mitsicherungen zu vermeiden.[cite:43]
Bei Verwendung von `--delete` gilt: Gelöschte Dateien verschwinden aus dem neu erzeugten Snapshot, bleiben aber in älteren Snapshots weiterhin vorhanden.[cite:34][cite:92] Genau dadurch entsteht die gewünschte Snapshot-Historie.[cite:92]
## Mögliche Erweiterungen
Sinnvolle nächste Ausbaustufen wären ein optionaler `--dry-run`-Schalter, eine Prüfung des tatsächlichen Mountpoints per `mountpoint`, sowie eine Automatisierung über `systemd`-Timer statt manueller Ausführung.[cite:97][cite:43]