ZFS in der Praxis: Snapshots, Scrub, Replikation und Pool-Tuning richtig nutzen
ZFS-Snapshots sind kein Backup: Erst die Replikation auf einen physisch getrennten Host schützt vor Datenverlust. Die Anleitung zeigt Snapshots, Retention mit sanoid/syncoid, Scrubs per systemd-Timer und Pool-Tuning (ashift, recordsize, LZ4, ARC).
Geprüft am 11.10.2026 · für OpenZFS 2.4.4, sanoid 2.3.0
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

ZFS schützt Daten auf Linux-Servern zuverlässig, wenn vier Bausteine zusammenspielen: Snapshots, Replikation mit sanoid/syncoid auf einen anderen Host, regelmäßige Scrubs und ein zum Workload passendes Pool-Tuning. Der häufigste Irrtum: Snapshots im selben Pool als „Backup“ zu betrachten.
Voraussetzungen
- Linux mit OpenZFS 2.2 oder neuer: Ubuntu 24.04, Debian 12/13, Proxmox VE 8 oder neuer (
apt install zfsutils-linux) oder FreeBSD 14 und neuer (nativ integriert) - Root- oder sudo-Zugang auf Produktions- und Backup-Host
- SSH-Schlüssel-Authentifizierung zwischen Quell- und Ziel-Host (kein Passwort-Prompt im Cronjob), siehe SSH-Key-Authentifizierung einrichten
- sanoid auf beiden Hosts:
apt install sanoid(Ubuntu/Debian, Paket enthält auch syncoid) oder aktuelles Release (v2.3.0) von GitHub - Optional:
apt install mbufferfür große Netzwerk-Transfers - Separate physische Hardware für den Backup-Host, kein Backup-Pool im selben Gehäuse wie Produktion
Schritt 1: Pool korrekt erstellen, ashift von Anfang an richtig setzen
ashift bestimmt die minimale Blockgröße je vdev und ist nach der Pool-Erstellung unveränderlich. Ein zu kleiner Wert, z. B. ashift=9 (512 Byte) auf Datenträgern mit 4K-Sektoren, erzwingt Read-Modify-Write und kostet dauerhaft Leistung. Abhilfe schafft nur, den Pool neu zu erstellen.
- HDDs und SSDs mit 4K-Sektoren (aktueller Standard):
ashift=12 - Manche NVMe-SSDs mit 8K-Seiten:
ashift=13 - Verwenden Sie immer Pfade unter
/dev/disk/by-id/, damit nach einem Neustart die richtige Platte zugeordnet wird
# Mirror-Pool mit ashift=12
zpool create -o ashift=12 tank mirror \
/dev/disk/by-id/ata-SSD_A \
/dev/disk/by-id/ata-SSD_B
# LZ4-Komprimierung am Root-Dataset aktivieren (alle Children erben)
zfs set compression=lz4 tank
LZ4 ist schnell genug, um praktisch nie zum Engpass zu werden; bei nicht komprimierbaren Daten bricht ZFS die Komprimierung früh ab. Aktivieren Sie LZ4 daher am Pool-Root.
Verifizieren: zpool get ashift tank zeigt 12, zfs get compression tank zeigt lz4.
Schritt 2: recordsize workload-spezifisch setzen
Die Standard-recordsize in OpenZFS beträgt 128K. Für Datenbanken mit kleinen, zufälligen Schreibzugriffen verursacht dieser Wert unnötige Write Amplification. Eine Änderung wirkt nur auf neu geschriebene Blöcke; Bestandsdaten behalten ihre Blockgröße.
| Workload | Empfohlene recordsize | Begründung |
|---|---|---|
| MySQL/MariaDB InnoDB (Daten) | 16K | Entspricht der InnoDB-Seitengröße |
| InnoDB-Logs | 128K (Default) | Große sequenzielle Writes |
| PostgreSQL (Daten und WAL) | 32K (64K oder 128K ebenfalls gut) | Mit LZ4 bringt Komprimierung mehr als eine an die 8K-Seite angepasste recordsize |
| VM-Images (qcow2) | 64K | Entspricht der Standard-Clustergröße von qcow2 |
| Sequenziell / NAS / Archiv | 1M | Maximiert sequenziellen Durchsatz |
zfs set recordsize=16K tank/mysql-data
zfs set recordsize=128K tank/mysql-logs
zfs set recordsize=32K tank/postgres
zfs set recordsize=64K tank/vms
zfs set recordsize=1M tank/archiv
Verifizieren: zfs get -r recordsize tank zeigt je Dataset den gesetzten Wert mit Quelle local.
Schritt 3: ARC-Limit konfigurieren
Der ARC (Adaptive Replacement Cache) nutzt freien RAM automatisch. Das Standard-Maximum hängt von der Version ab: Bis OpenZFS 2.2 (z. B. Ubuntu 24.04) ist es unter Linux die Hälfte des RAMs, ab 2.3 der größere Wert aus „RAM minus 1 GiB“ und „5/8 des RAMs“. Laufen neben ZFS speicherhungrige Dienste (VMs, Datenbanken), setzen Sie ein festes Maximum. Ein L2ARC-Cachegerät belegt zusätzlich RAM für seine Verwaltungsdaten; auf RAM-knappen Systemen kann es den ARC verdrängen.
# ARC-Maximum dauerhaft auf 16 GiB begrenzen
echo "options zfs zfs_arc_max=17179869184" | sudo tee /etc/modprobe.d/zfs.conf
# Sofort anwenden, ohne Neustart
echo 17179869184 | sudo tee /sys/module/zfs/parameters/zfs_arc_max
Liegt der Root-Pool auf ZFS, aktualisieren Sie zusätzlich die Initramfs (update-initramfs -u), damit der Wert beim Booten greift. Der ARC schrumpft nach dem Senken erst unter Speicherdruck.
Verifizieren: arc_summary | grep -i "max size" oder cat /sys/module/zfs/parameters/zfs_arc_max zeigt den neuen Wert.
Schritt 4: Snapshots verstehen und manuell nutzen
Snapshots sind schreibgeschützte Copy-on-Write-Abbilder eines Datasets zum Erstellungszeitpunkt. Anfangs belegen sie keinen Platz, danach wachsen sie mit jedem geänderten Block. Snapshots im selben Pool sind kein Backup: Geht der Pool verloren (mehr Plattenausfälle als die Redundanz verkraftet, Brand, Controller-Defekt), sind Daten und Snapshots gemeinsam weg.
# Snapshot manuell anlegen
zfs snapshot tank/daten@manuell-2026-09-30
# Snapshots mit Platzverbrauch auflisten
zfs list -t snapshot -o name,used,referenced,creation -s creation -r tank
# Platz durch Snapshots je Dataset anzeigen
zfs list -o name,used,usedbysnapshots -r tank
# Auf den letzten Snapshot zurückrollen (Änderungen danach gehen verloren)
zfs rollback tank/daten@manuell-2026-09-30
# Snapshot löschen
zfs destroy tank/daten@manuell-2026-09-30
Einzelne Dateien kopieren Sie ohne Rollback aus .zfs/snapshot/<name>/ im Mountpoint.
Achtung Speicherfalle: Ohne automatisches Pruning wächst der Platzbedarf der Snapshots, bis der Pool voll ist. Prüfen Sie regelmäßig die Spalte usedbysnapshots.
Verifizieren: zfs list -t snapshot -r tank/daten zeigt den angelegten Snapshot.
Schritt 5: sanoid für automatisierte Retention konfigurieren
sanoid (aktuell v2.3.0) erstellt und löscht Snapshots nach Regeln aus /etc/sanoid/sanoid.conf. Legen Sie die Datei auf Produktions- und Backup-Host an; hier beide Abschnitte in einem Beispiel:
# /etc/sanoid/sanoid.conf
# Produktions-Host
[tank/daten]
use_template = produktion
recursive = yes
[template_produktion]
frequently = 0
hourly = 36
daily = 30
weekly = 4
monthly = 3
yearly = 0
autosnap = yes
autoprune = yes
# Backup-Host
[backup/daten]
use_template = backup_empfang
recursive = yes
[template_backup_empfang]
frequently = 0
hourly = 36
daily = 90
weekly = 4
monthly = 6
yearly = 0
autosnap = no
autoprune = yes
Das Template backup_empfang mit autosnap = no sorgt dafür, dass der Backup-Host längere Aufbewahrungsfristen hat, ohne eigene Snapshots zu erstellen. Das Debian/Ubuntu-Paket bringt den systemd-Timer sanoid.timer mit, der sanoid alle 15 Minuten ausführt. Bei manueller Installation aus GitHub richten Sie den Timer nach der INSTALL.md des Projekts ein. sanoid arbeitet dort mit TZ=UTC, um Probleme bei der Zeitumstellung zu vermeiden.
sudo systemctl enable --now sanoid.timer
Verifizieren: systemctl list-timers sanoid.timer zeigt den nächsten Lauf; nach spätestens einer Stunde listet zfs list -t snapshot -r tank/daten Snapshots mit dem Präfix autosnap_.
Schritt 6: Replikation mit syncoid einrichten
syncoid gehört zum sanoid-Paket und steuert intern zfs send/receive.
Push oder Pull: Beim Push braucht der Produktionsserver Schreibrechte auf dem Backup-Host; ein kompromittierter Produktionsserver kann die Sicherung dann überschreiben. Beim Pull holt der Backup-Server die Daten aktiv. Pull ist die sicherere Wahl. Verbindet sich syncoid nicht als root, delegieren Sie auf dem Quellsystem die Rechte mit zfs allow (auf der Quelle mindestens send).
# /etc/cron.d/syncoid auf dem Backup-Server
# Lokale Replikation in einen zweiten Pool (stündlich, Minute 2)
02 * * * * root syncoid -r --no-sync-snap --compress=lzo --quiet tank/daten backup/daten
# Pull vom Produktionsserver per SSH-Schlüssel (stündlich, Minute 20)
20 * * * * root syncoid --sshkey=/root/.ssh/syncoid -r --no-sync-snap --compress=lzo --quiet syncoid@prodserver:tank/daten backup/prodserver/daten
--no-sync-snap beschränkt syncoid auf vorhandene sanoid-Snapshots, statt eigene Hilfs-Snapshots anzulegen. Für große manuelle Transfers glättet mbuffer Schwankungen im Datenfluss:
zfs send -R -i tank/daten@v1 tank/daten@v2 \
| mbuffer -s 128k -m 1G \
| ssh backup 'zfs receive -u backup/daten'
Verifizieren: Führen Sie den syncoid-Befehl einmal manuell ohne --quiet aus; danach zeigt zfs list -t snapshot -r backup/prodserver/daten auf dem Backup-Server dieselben Snapshots wie die Quelle.
Schritt 7: zfs send/receive direkt nutzen
Ohne sanoid steuern Sie zfs send/receive selbst. Die wichtigsten Flags:
| Flag | Bedeutung |
|---|---|
-i snap1 snap2 | Delta zwischen genau zwei Snapshots, ohne Zwischen-Snapshots |
-I snap1 snap2 | Überträgt alle Zwischen-Snapshots, die Snapshot-Historie bleibt erhalten |
-R | Repliziert den kompletten Dataset-Baum inkl. Properties |
-w | Raw Send verschlüsselter Datasets, der Empfänger braucht keinen Schlüssel |
-n -v | Dry-Run: schätzt die Größe ohne Übertragung |
# Erstsicherung (Full Send) auf den Backup-Host
zfs send -R tank/daten@snap-v1 | ssh backup 'zfs receive -u -F backup/daten'
# Inkrementeller Send; -s beim Empfänger erlaubt das Fortsetzen nach Abbruch
zfs send -i tank/daten@snap-v1 tank/daten@snap-v2 \
| ssh backup 'zfs receive -s -u backup/daten'
# Dry-Run: Größe eines inkrementellen Sends schätzen
zfs send -n -v -i tank/daten@snap-v1 tank/daten@snap-v2
# Verschlüsseltes Dataset roh übertragen
zfs send -w tank/geheim@snap-v1 | ssh backup 'zfs receive -u backup/geheim'
# Abgebrochenen Transfer fortsetzen (nur mit receive -s)
TOKEN=$(ssh backup 'zfs get -H -o value receive_resume_token backup/daten')
zfs send -t "$TOKEN" | ssh backup 'zfs receive -s -u backup/daten'
# Bookmark als Replikationsanker anlegen
zfs bookmark tank/daten@snap-v2 tank/daten#repl-base
Bookmarks (#name statt @name) erhalten den Anker für inkrementelle Sends, auch wenn Sie den Snapshot auf dem Sender löschen.
Verifizieren: ssh backup 'zfs list -t snapshot -r backup/daten' zeigt snap-v1 und snap-v2.
Schritt 8: Scrubs gegen Bit-Rot planen
Ein Scrub liest jeden belegten Block und prüft seine Prüfsumme. Mit Redundanz (Mirror, RAIDZ) repariert ZFS fehlerhafte Blöcke automatisch. Ein Scrub erhöht die I/O-Last; planen Sie ihn in Zeiten geringer Nutzung.
# Scrub manuell starten, Status prüfen
zpool scrub tank
zpool status -v tank
# Laufenden Scrub pausieren und später fortsetzen
zpool scrub -p tank
zpool scrub tank
Für automatische Scrubs liefert OpenZFS die Timer zfs-scrub-monthly@.timer und zfs-scrub-weekly@.timer mit (Persistent=true, verpasste Läufe werden nachgeholt):
sudo systemctl enable --now zfs-scrub-monthly@tank.timer
Unter Debian und Ubuntu startet das Paket zfsutils-linux zusätzlich per /etc/cron.d/zfsutils-linux am zweiten Sonntag jedes Monats einen Scrub aller Pools. Nutzen Sie nur einen der beiden Wege, damit keine doppelten Scrubs laufen. Empfohlener Rhythmus: monatlich für kleine Pools, alle ein bis zwei Wochen für große Produktions-Pools. Nach jedem Festplatten-, HBA- oder Kabelwechsel starten Sie einen manuellen Scrub.
Verifizieren: systemctl list-timers 'zfs-scrub*' zeigt den nächsten Termin; zpool status tank meldet nach dem Lauf scrub repaired 0B … with 0 errors.
Troubleshooting / Typische Fehler
- „No space left on device“ trotz gelöschter Dateien: Alte Snapshots halten den Platz belegt. Prüfen Sie
zfs list -o name,usedbysnapshots -r tankund löschen Sie alte Snapshots oder aktivieren Sieautoprune = yesin sanoid. - „cannot receive incremental stream: most recent snapshot does not match incremental source“: Der gemeinsame Basis-Snapshot fehlt auf einer Seite. Nutzen Sie Bookmarks als Anker oder stimmen Sie die Retention auf Sender und Empfänger so ab, dass der Basis-Snapshot erst nach der nächsten Replikation gelöscht wird.
- CKSUM-Fehler in
zpool status: ZFS erkennt auch Kabel-, Controller- und RAM-Fehler, die SMART nicht meldet. Setzen Sie die Zähler nicht einfach mitzpool clearzurück, sondern prüfen Sie zuerst Kabel, HBA und RAM. - Empfangenes Dataset wird eingehängt: Ohne
-ubeimzfs receiveodercanmount=offauf dem Backup-Pool können Indexer oder Virenscanner in Backup-Datasets schreiben; das bricht die nächste inkrementelle Übertragung. Verwenden Sie immerzfs receive -u. - recordsize-Änderung zeigt keine Wirkung: Der Wert gilt nur für neue Schreibvorgänge. Bestandsdaten schreiben Sie neu, z. B. per Dump und Restore (
pg_dump,mysqldump). - Scrub erhöht die Latenz: Verschieben Sie den Termin in ein Wartungsfenster oder pausieren Sie den laufenden Scrub mit
zpool scrub -p tank.
Häufige Fragen
Ist ein ZFS-Snapshot ein Backup?
Nein. Er liegt im selben Pool wie die Daten; erst eine Kopie per zfs send/receive auf einen getrennten Host ist ein Backup.
Wie oft sollten Sie scrubben?
Monatlich für kleine Pools, alle ein bis zwei Wochen für große Produktions-Pools, zusätzlich nach jedem Hardwarewechsel.
Lässt sich recordsize nachträglich ändern?
Ja, mit zfs set recordsize=…, aber nur mit Wirkung auf neu geschriebene Daten.
Warum syncoid statt direktem zfs send/receive?
syncoid ermittelt den gemeinsamen Basis-Snapshot, wählt inkrementelle Übertragung, setzt abgebrochene Transfers fort und komprimiert unterwegs. Ein eigenes Skript muss all das selbst abbilden und ist fehleranfälliger.
Was ist der Unterschied zwischen zfs send -i und -I?
-i sendet nur die Differenz zwischen zwei Snapshots. -I überträgt zusätzlich alle Zwischen-Snapshots; damit können Sie auch auf dem Empfänger aus jedem Zwischenstand wiederherstellen.
Wie viel RAM braucht der ARC wirklich?
ZFS läuft auch mit wenigen GB RAM; mehr RAM erhöht die Trefferquote des Caches. Eine feste Regel gibt es nicht. Beobachten Sie mit arc_summary die Hit-Rate: Bleibt sie hoch, reicht der ARC. Deduplizierung braucht deutlich mehr RAM (etwa 320 Byte je eindeutigem Block) und lohnt sich nur in Sonderfällen.
Fazit
Drei Entscheidungen wirken dauerhaft: ashift (nach der Pool-Erstellung unveränderlich), recordsize (gilt nur für neue Blöcke) und die Replikationsrichtung (Pull statt Push). Mit sanoid/syncoid und dem mitgelieferten Scrub-Timer läuft der Betrieb weitgehend automatisch, und Snapshots und Backup bleiben sauber getrennt.
Weiterführende Anleitungen und Quellen
- Festplatten, Partitionen, LVM und fstab unter Linux
- SSH-Key-Authentifizierung einrichten (Linux & Windows)
- systemd-Services erstellen und verwalten
- Linux-Server absichern mit UFW und fail2ban
Quellen: OpenZFS Workload Tuning | OpenZFS Modulparameter (zfs.4) | Klara Systems: Tuning recordsize in OpenZFS | GitHub: jimsalterjrs/sanoid | oxcrag.net: ZFS Backup Strategy with Sanoid and Syncoid


