Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Linux 04.06.2026 · 10 min Lesezeit

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

Storage-Server mit Festplatten in Wechselrahmen, ein Laufwerk halb herausgezogen, grüne und blaue Status-LEDs

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 mbuffer fü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.

WorkloadEmpfohlene recordsizeBegründung
MySQL/MariaDB InnoDB (Daten)16KEntspricht der InnoDB-Seitengröße
InnoDB-Logs128K (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)64KEntspricht der Standard-Clustergröße von qcow2
Sequenziell / NAS / Archiv1MMaximiert 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:

FlagBedeutung
-i snap1 snap2Delta zwischen genau zwei Snapshots, ohne Zwischen-Snapshots
-I snap1 snap2Überträgt alle Zwischen-Snapshots, die Snapshot-Historie bleibt erhalten
-RRepliziert den kompletten Dataset-Baum inkl. Properties
-wRaw Send verschlüsselter Datasets, der Empfänger braucht keinen Schlüssel
-n -vDry-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 tank und löschen Sie alte Snapshots oder aktivieren Sie autoprune = yes in 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 mit zpool clear zurück, sondern prüfen Sie zuerst Kabel, HBA und RAM.
  • Empfangenes Dataset wird eingehängt: Ohne -u beim zfs receive oder canmount=off auf dem Backup-Pool können Indexer oder Virenscanner in Backup-Datasets schreiben; das bricht die nächste inkrementelle Übertragung. Verwenden Sie immer zfs 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

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