Wir setzen bei unseren Kunden bereits seit Jahren auf Proxmox VE als Hypervisor-System, nicht erst seit der Übernahme von VMWare durch Broadcom. Im Laufe der Zeit stolpert man über einige Fallstricke, die man vermeiden sollte. Dazu gehört u.a. das ZFS-System, soz. ein „all-in-one“-Mix aus RAID und Dateisystem. Wer mehr zu ZFS erfahren möchte, seien die Links bei Storage Insider und Thomas Krenn zu empfehlen.
Wer sich länger mit Proxmox VE und OpenZFS beschäftigt, merkt relativ schnell: im Internet kursieren unglaublich viele veraltete Empfehlungen.
Vor allem beim Thema:
- RAIDZ vs. Mirror
- volblocksize
- SSD-Auswahl
- ARC-Größe
- „4K überall!“
- Consumer-SSDs im Serverbetrieb
…werden bis heute Tipps wiederholt, die entweder aus HDD-Zeiten stammen oder aus völlig anderen Workloads kommen.
Dieser Beitrag richtet sich an reale Proxmox-Umgebungen im Jahr 2026:
- NVMe-Storage
- Enterprise-SSDs
- Windows Server 2022/2025
- Linux-VMs
- Docker-Hosts
- Datenbanken
- Backup-Server
- hohe VM-Dichte
- produktive Infrastruktur
Und vor allem: nicht an synthetische CrystalDiskMark-Spielereien.
RAIDZ oder (striped) Mirror?
Die wichtigste Regel zuerst:
Für produktiven VM-Storage sind Mirrors fast immer die bessere Wahl.
Ja, RAIDZ funktioniert technisch problemlos mit VMs. Aber performant, konsistent und latenzarm wird es oft erst mit sehr viel Hardwareeinsatz.
Gerade kleine Random-IOs sind das Problem.
VMs erzeugen:
- viele kleine Schreibzugriffe
- Metadatenzugriffe
- parallele IO-Muster
- gemischte Lasten
RAIDZ muss dabei Parität berechnen und Daten auf mehrere Disks verteilen. Das kostet IOPS und erhöht die Latenz.
Mirrors verhalten sich deutlich direkter und vorhersehbarer.
Das merkt man besonders bei:
- vielen gleichzeitig laufenden VMs
- Datenbanken
- Windows-Servern
- Docker-Hosts
- RDS-Systemen
- Fileservern
- Mailservern
Warum RAIDZ-Setups "träge" wirken
Das Problem ist oft nicht die rohe Bandbreite.
Ein RAIDZ1/2 aus modernen NVMe-SSDs erreicht problemlos mehrere GB/s.
Die eigentliche Schwäche liegt bei:
- kleinen Random-IOs
- Sync-Writes
- gemischten VM-Lasten
- hoher Parallelität
Genau dort leben Hypervisoren. Deshalb wirken viele RAIDZ-Systeme:
- „hakelig“
- inkonsistent
- latenzanfällig
obwohl Benchmarks hohe Durchsätze zeigen.
Empfehlungen für produktiven VM-Storage
Für echte Virtualisierung bevorzugen wir inzwischen fast immer:
- NVMe/SSD-Mirror
- oder mehrere gestripte Mirrors
Beispiel:
- 2× NVMe → Mirror
- 4× NVMe → 2 Mirrors + Striping
- 6× NVMe → 3 Mirrors + Striping
Das liefert:
- bessere Latenzen
- bessere Skalierung
- stabilere IOPS
- schnellere Resilver
- besser vorhersehbare Performance
Und vor allem: weniger Überraschungen unter Last.
Enterprise SSDs - im produktiven Umfeld Pflicht
Das wird extrem häufig unterschätzt und bspw. im Forum immer wieder diskutiert. Im einfachen Homelab mag man mit Consumer SSDs hinkommen, im produktiven Umfeld sollten solche Laufwerke nicht zum Einsatz kommen.
Zwischen einer Consumer-NVMe und einer Enterprise-SSD liegen Welten.
Nicht unbedingt bei:
- sequentieller Leserate
- Marketing-Benchmarks
Sondern bei:
- QoS
- Write-Latenz
- Sustained Performance
- Power Loss Protection
- Garbage Collection
- Dauerlast
- Konsistenz
Gerade ZFS reagiert empfindlich auf schlechte SSD-Latenzen.
Eine billige Consumer-SSD kann ein gesamtes Pool-Verhalten ruinieren.
Das merkt man dann als:
- IO-Delays
- sporadische Freezes
- hohe Latenzen
- „langsames Gefühl“
- schlechte VM-Responsiveness
Welche SSDs eignen sich?
Für produktive Systeme raten wir klar zu Enterprise-Modellen.
Beispielsweise:
- Samsung PM1735
- Samsung PM9A3
- Kioxia CD6
- Solidigm D7 Series
- Kingston DC600M
Gerade Power Loss Protection ist im ZFS-Umfeld extrem wichtig.
volblocksize – das meistmissverstandene Thema
Zum eigentlichen Dauerbrenner…
Früher wurde oft empfohlen:
- 8K
- 16K
- maximal 16K für Windows
Begründung:
„NTFS nutzt 4K Clustergröße.“
Das ist technisch nicht falsch — aber in modernen Umgebungen oft trotzdem nicht mehr optimal.
Warum kleine volblocksizes früher beliebt waren
Die Idee dahinter war simpel:
Wenn Windows intern 4K schreibt und ZFS 64K-Blöcke nutzt, muss ZFS größere Bereiche verändern.
Das erzeugt:
- Read-Modify-Write
- mehr Write Amplification
- zusätzliche IOs
Das war früher bei:
- HDDs
- SATA-SSDs
- schwachen Controllern
- kleinen ARC-Größen
…ein echtes Problem.
Aktuelle Empfehlungen für volblocksize
Für moderne Enterprise-NVMe-Mirror-Setups nutzen wir inzwischen oft:
64K als Standard für:
- Windows Server
- Linux-Server
- Fileserver
- Infrastruktur-VMs
- allgemeine Produktivsysteme
16K eher noch für:
- Docker-Hosts
- Kubernetes-Nodes
- sehr IO-intensive Kleinfile-Workloads
- Spezialanwendungen
8K oder kommen meist bei Sonderfällen (bspw. XFS Partitionen für Logging o.ä.) zum Einsatz
Kleine volblocksizes auf RAIDZ können katastrophal sein
Das ist ein Punkt, den viele unterschätzen.
Kleine Blöcke auf RAIDZ verursachen:
- extrem hohen Overhead
- schlechte Speicherplatzeffizienz
- zusätzliche Paritätsoperationen
- unnötige Fragmentierung
Gerade 4K oder 8K auf RAIDZ-VM-Storages können die Performance massiv verschlechtern.
Benchmark-Fallen
Viele User optimieren ihr Setup anhand von:
- CrystalDiskMark
- ATTO
- synthetischen Random-Tests
Das Problem:
Diese Benchmarks bestrafen große volblocksizes oft künstlich. Produktive VM-Workloads verhalten sich aber komplett anders.
Ein System mit schlechteren 4K-QD32-Werten kann sich im Alltag trotzdem deutlich schneller anfühlen.
ARC - häufig unterschätzt
Kleine Blöcke bedeuten:
- mehr Metadaten
- mehr Verwaltungsaufwand
- schlechtere Cache-Effizienz
Größere volblocksizes entlasten den ARC häufig deutlich.
Gerade Hosts mit:
- vielen VMs
- hoher Dichte
- vielen Snapshots
profitieren oft von größeren Blöcken.
ashift richtig setzen & Compression aktivieren
Für moderne SSDs und NVMe sollte praktisch immer gelten:
- ashift=12
- oder in seltenen Fällen ashift=13
Ein falsch gesetztes ashift lässt sich später nicht mehr ändern.
Das Pool-Layout sollte deshalb von Anfang an sauber geplant werden.
Sollte man Compression aktivieren? Ja, Fast immer!
LZ4 kostet auf moderner Hardware praktisch nichts mehr.
Vorteile:
- weniger Schreiblast
- weniger SSD-Verschleiß
- bessere effektive Performance
- geringerer Speicherverbrauch
Gerade bei:
- Windows-VMs
- Logs
- Office-Daten
- Datenbanken
funktioniert Compression oft überraschend gut.
ZVOL oder Dataset?
VM-Disks in Proxmox liegen typischerweise als zvols vor.
Das ist wichtig, weil:
- zvols blockbasiert arbeiten
- Datasets dateibasiert arbeiten
Viele Empfehlungen im Internet vermischen diese beiden Welten ständig. Dadurch entstehen oft falsche Schlussfolgerungen.
SLOG - of missverstanden
Kaum ein ZFS-Thema wird so häufig falsch verstanden wie der SLOG.
Viele glauben: „Wenn ich eine schnelle NVMe als SLOG einbaue, wird alles schneller!“
Das stimmt so nicht.
Funktionen des SLOG
SLOG bedeutet: Separate Intent Log.
Er beschleunigt ausschließlich synchrone Schreibzugriffe.
Nicht:
- normale Reads
- asynchrone Writes
- allgemeine Random-IO
- Bootzeiten
- normale Desktop-Workloads
Und genau hier liegt das Missverständnis.
Wann SLOG etwas bringt
Ein SLOG kann massiv helfen bei:
- NFS mit sync writes
- Datenbanken
- VMware-Workloads
- bestimmten VM-Setups
- Storage-Servern
- ESXi über NFS/iSCSI
- sehr write-latenzempfindlichen Anwendungen
Homelab-SLOG vs. "guter" SLOG
Oft sieht man:
- langsame SATA-SSD als SLOG
- Consumer-NVMe ohne PLP
- einzelne SSD ohne Mirror
- „weil man das halt so macht“
Das bringt häufig:
- keinen echten Vorteil
- zusätzliche Komplexität
- schlechtere Stabilität
Oder sogar schlechtere Performance.
Ein SLOG muss vor allem:
- niedrige Write-Latenzen
- konstante QoS
- Power Loss Protection
- hohe Haltbarkeit
haben. Deshalb eignen sich typischerweise:
- Intel Optane
- hochwertige Enterprise-NVMe
Sehr gut waren beispielsweise:
- Intel Optane P4801X
- Intel Optane P5800X
Normale Consumer-NVMe sind oft ungeeignet.
SLOG ersetzt keinen schnellen Pool
Das ist extrem wichtig.
Ein langsamer RAIDZ-Pool wird durch einen SLOG nicht plötzlich schnell.
Der SLOG puffert nur bestimmte Schreiboperationen. Die eigentlichen Daten landen weiterhin im Hauptpool.
SLOG im Mirror
In Produktivsystemen sollte ein SLOG immer als Mirror vorhanden sein. Ist nur ein einzelnes SLOG device/drive vorhanden, kann das bei einem Ausfall schnell unangenehm werden.
Special Device - sinnvoll oder unnötig?
Das ZFS Special Device wird häufig als „Performance-Wunder“ dargestellt. In der Praxis hängt der tatsächliche Nutzen aber extrem vom zugrundeliegenden Storage ab.
Funktionen des Special Device
Ein Special Allocation Device lagert bestimmte Daten auf einem bzw. mehreren separaten, schnellen Datenträger(n) aus.
Dazu gehören:
- Metadaten
- kleine Blöcke
- DDTs bei Deduplication
- Verzeichnisstrukturen
- teilweise Snapshot-Metadaten
Dadurch müssen diese Zugriffe nicht mehr vom eigentlichen Hauptpool bedient werden.
Special Device bei "langsamen" Pools
Klassische HDD-Pools leiden massiv unter:
- langsamen Random-IOs
- Metadatenzugriffen
- vielen kleinen Dateien
- Snapshot-Operationen
Genau dort hilft ein schnelles NVMe-Special-Device enorm.
Typische Vorteile:
- deutlich schnellere Directory-Listings
- schnellere Dateioperationen
- weniger „träge“ wirkende Pools
- bessere Snapshot-Performance
- schnellere Metadatenzugriffe
- subjektiv deutlich reaktionsfreudigeres System
Gerade bei:
- großen HDD-RAIDZ-Pools
- Backup-Storage
- Archivsystemen
- gemischten Workloads
- vielen kleinen Dateien
kann ein Special Device extrem viel bringen. Auf „All-Flash-Speichern“ mit von Haus aus niedrigen Latenzen und hohen IOPS bringt ein Special Device meist kaum messbare Vorteile. Die Geschwindigkeit des Special Device muss die des Pools schon um einen wesentlichen Faktor übertreffen, um Vorteile erzielen zu können.
Wichtigester Punkt: Special Device ist kritisch!
Fällt ein ungespiegeltes Special Device aus, kann der gesamte Pool unbrauchbar werden.
Deshalb gilt produktiv praktisch immer:
- Mirror
- Enterprise-SSD/NVMe
- keine Consumer-SSDs
- keine Einzeldevices
Nützliche Befehle & Scripts
Proxmox VE
- Script zur Abfrage aller volblocksizes pro VM in Proxmox VE
#!/bin/bash
echo -e "ZVOL\t\t\tVMID\tSIZE\tUSED\tvolblocksize"
echo "---------------------------------------------------------------"
zfs list -t volume -o name,volsize,used | grep -E "vm-[0-9]+-disk" | while read -r line; do
ZVOL=$(echo "$line" | awk '{print $1}')
SIZE=$(echo "$line" | awk '{print $2}')
USED=$(echo "$line" | awk '{print $3}')
VMID=$(echo "$ZVOL" | grep -oP 'vm-\K[0-9]+')
BLOCKSIZE=$(zfs get -H -o value volblocksize "$ZVOL")
echo -e "$ZVOL\t$VMID\t$SIZE\t$USED\t$BLOCKSIZE"
done
Das Script muss nach dem Erstellen mittels chmod +x get_volblocksize.sh ausführbar gemacht werden. Nach dem Aufruf via ./get_volblocksize.sh sieht die Ausgabe bspw. so aus:
ZVOL VMID SIZE USED volblocksize
---------------------------------------------------------------
rpool/data/vm-100-disk-0 100 32G 2.5G 16K
rpool/data/vm-101-disk-0 101 64G 5.1G 64K
...
Es ist kompatibel mit allen Pools und zeigt auch den verwendeten Speicher an.
- Script zur Migration eines ZVOLs in eine neue volblocksize
Mit diesem Script kann das ZVOL einer VM auf eine neue volblocksize angepasst werden.
Hinweis: das Script „kopiert“ das bestehende ZVOL 1:1 und prüft mittels SHA256 Quelle und Ziel. Es sollte ausreichend Speicherplatz vorhanden sein.
Der Aufruf erfolgt bspw. wie folgt:
./migrate_zvol_blocksize.sh <ID DER VM> <DISK NUMMER> <VOLBLOCKSIZE>
Im Klartext:
./migrate_zvol_blocksize.sh 101 0 16K
Dies würde Disk 0 von VM 101 auf ein neues ZVOL mit 16K volblocksize kopieren. Das neue ZVOL erscheint dann als vm-101-disk-0-migrated
⚠ Diese Variablen sollten vorab angepasst werden: ⚠
POOL="DEIN-ZFS-POOLNAME" # ZFS-Poolnamen anpassen
...
dd if="/dev/zvol/${ORIG_ZVOL}" of="/dev/zvol/${NEW_ZVOL}" bs=16M status=progress conv=sync
bs=16M entspricht der Blockgröße während des Kopierens. Dieser Wert ist für leistungsfähige NVME angepasst und sollte bei langsameren Laufwerken nach unten korrigiert werden. Der gesamte Migrationsvorgang kann (je nach Größe des ZVOLs und Leistungsfähigkeit des Systems) sehr lange dauern. Der größte Zeitanteil entfällt dabei auf die SHA256-Prüfung.
Variante 1 (mit SHA256-Prüfung):
#!/bin/bash
# ⚙ Migration eines ZVOLs mit neuer volblocksize (z. B. 16K)
# Nutzung: ./migrate_zvol_blocksize.sh <VMID> <Disk-ID> <Neue-Blocksize>
# Beispiel: ./migrate_zvol_blocksize.sh 101 0 16K
set -euo pipefail
POOL="DEIN-ZFS-POOLNAME" # ZFS-Poolnamen anpassen
VMID="$1"
DISK_ID="$2"
NEW_BLOCKSIZE="$3"
ORIG_ZVOL="${POOL}/vm-${VMID}-disk-${DISK_ID}"
SNAP_NAME="migrate_$(date +%Y-%m-%d_%H-%M)"
SNAPSHOT="${ORIG_ZVOL}@${SNAP_NAME}"
NEW_ZVOL="${ORIG_ZVOL}-migrated"
echo "🔍 Prüfe Quell-ZVOL ${ORIG_ZVOL} ..."
zfs list -t volume "${ORIG_ZVOL}" > /dev/null
# Optional: prüfen, ob Ziel-ZVOL bereits existiert
if zfs list "${NEW_ZVOL}" &>/dev/null; then
echo "⚠️ Ziel-ZVOL ${NEW_ZVOL} existiert bereits."
read -rp "❓ Möchtest du es löschen? (y/N): " confirm
if [[ "${confirm,,}" == "y" ]]; then
echo "🗑️ Lösche vorhandenes ZVOL..."
zfs destroy -r "${NEW_ZVOL}"
else
echo "❌ Abbruch, um Überschreiben zu vermeiden."
exit 1
fi
fi
SIZE=$(zfs get -H -o value volsize "$ORIG_ZVOL")
echo "📸 Erstelle Snapshot: $SNAPSHOT"
zfs snapshot "$SNAPSHOT"
echo "📦 Erstelle Ziel-ZVOL ${NEW_ZVOL} mit volblocksize=${NEW_BLOCKSIZE} ..."
zfs create -V "$SIZE" -b "$NEW_BLOCKSIZE" "$NEW_ZVOL"
echo "🔁 Kopiere Daten mit dd ..."
dd if="/dev/zvol/${ORIG_ZVOL}" of="/dev/zvol/${NEW_ZVOL}" bs=16M status=progress conv=sync
echo "🔐 Prüfe SHA256 (Quelle vs. Ziel) ..."
SRC_HASH=$(sha256sum "/dev/zvol/${ORIG_ZVOL}" | cut -d ' ' -f1)
DST_HASH=$(sha256sum "/dev/zvol/${NEW_ZVOL}" | cut -d ' ' -f1)
echo "💠 Quelle: $SRC_HASH"
echo "💠 Ziel: $DST_HASH"
if [[ "$SRC_HASH" == "$DST_HASH" ]]; then
echo "✅ Prüfsumme OK. Migration erfolgreich."
echo "🧹 Entferne Snapshot $SNAPSHOT"
zfs destroy "$SNAPSHOT"
echo "❗ Neues ZVOL ist bereit: $NEW_ZVOL"
echo "👉 Du kannst es nun der VM manuell zuweisen oder das alte ZVOL ersetzen."
else
echo "❌ Prüfsummen stimmen nicht überein. Bitte manuell prüfen!"
exit 1
fi
Variante 2 (ohne SHA256-Prüfung):
#!/bin/bash
# ⚙ Migration eines ZVOLs mit neuer volblocksize (z. B. 16K) OHNE SHA256-Pruefung
# Nutzung: ./migrate_zvol_blocksize.sh <VMID> <Disk-ID> <Neue-Blocksize>
# Beispiel: ./migrate_zvol_blocksize.sh 101 0 16K
set -euo pipefail
POOL="DEIN-ZFS-POOLNAME" # ZFS-Poolnamen ggf. anpassen
VMID="$1"
DISK_ID="$2"
NEW_BLOCKSIZE="$3"
ORIG_ZVOL="${POOL}/vm-${VMID}-disk-${DISK_ID}"
SNAP_NAME="migrate_$(date +%Y-%m-%d_%H-%M)"
SNAPSHOT="${ORIG_ZVOL}@${SNAP_NAME}"
NEW_ZVOL="${ORIG_ZVOL}-migrated"
echo "🔍 Prüfe Quell-ZVOL ${ORIG_ZVOL} ..."
zfs list -t volume "${ORIG_ZVOL}" > /dev/null
# Optional: prüfen, ob Ziel-ZVOL bereits existiert
if zfs list "${NEW_ZVOL}" &>/dev/null; then
echo "⚠️ Ziel-ZVOL ${NEW_ZVOL} existiert bereits."
read -rp "❓ Möchtest du es löschen? (y/N): " confirm
if [[ "${confirm,,}" == "y" ]]; then
echo "🗑️ Lösche vorhandenes ZVOL..."
zfs destroy -r "${NEW_ZVOL}"
else
echo "❌ Abbruch, um Überschreiben zu vermeiden."
exit 1
fi
fi
SIZE=$(zfs get -H -o value volsize "$ORIG_ZVOL")
echo "📸 Erstelle Snapshot: $SNAPSHOT"
zfs snapshot "$SNAPSHOT"
echo "📦 Erstelle Ziel-ZVOL ${NEW_ZVOL} mit volblocksize=${NEW_BLOCKSIZE} ..."
zfs create -V "$SIZE" -b "$NEW_BLOCKSIZE" "$NEW_ZVOL"
echo "🔁 Kopiere Daten mit dd ..."
dd if="/dev/zvol/${ORIG_ZVOL}" of="/dev/zvol/${NEW_ZVOL}" bs=16M status=progress conv=sync
echo "✅ Migration abgeschlossen."
echo "🧹 Entferne Snapshot $SNAPSHOT"
zfs destroy "$SNAPSHOT"
echo "❗ Neues ZVOL ist bereit: $NEW_ZVOL"
echo "👉 Du kannst es nun der VM manuell zuweisen oder das alte ZVOL ersetzen."
- LBA size ermitteln und einstellen
Je nach Modell empfiehlt es sich, vor der Erstellung eines RAIDs die bestmögliche Formatierung (LBA-Größen) von SSD/NVME zu ermitteln und ggf. neu zu formatieren.
Bei der Verwendung von NVME wird nvme-cli benötigt. Dies kann mittels apt installiert werden:
apt install nvme-cli -y
/dev/nvme0n1): smartctl -a /dev/nvme0n1
Die Ausgabe sieht dann u.a. wie folgt aus:
...
Supported LBA Sizes (NSID 0x1)
Id Fmt Data Metadt Rel_Perf
0 + 512 0 2
1 - 4096 0 1
...
Zur Erläuterung:
- ID = fortlaufende Nummer von 0 – X
- Fmt = aktuelle Formatierung, gekennzeichnet durch das + Symbol
- Data = LBA-Größe
- Rel_Perf = dies ist der wichtigste Wert. Der ideale Wert ist 0. Je niedriger, desto besser
In diesem Fall wäre gemäß Rel_Perf der ideale Wert die Formatierung auf 4096 (ID = 1, Rel_Perf = 1). Zur Formatierung der NVME auf 4096 LBA-Größe diesen Befehl absetzen:
⚠ Achtung: alle Daten auf der NVME werden gelöscht! ⚠
nvme format /dev/nvme0n1 -l 1
Windows
- NTFS Clustergröße auslesen (in Windows VMs)
Variante 1 (CMD-Shell):
fsutil fsinfo ntfsinfo C:
In der Ausgabe auf „Bytes per Cluster“ achten (entspricht der NTFS-Clustergröße):
Bytes per Cluster : 4096
Bytes per Sector : 512
Variante 2 (PowerShell):
Get-WmiObject Win32_Volume | Where-Object { $_.DriveLetter -eq "C:" } | Select-Object DriveLetter, AllocationUnitSize
Um alle bzw. mehrere virtuellen NTFS-Volumes auszugeben, kann dieses Script verwendet werden:
Get-WmiObject Win32_Volume |
Where-Object { $_.FileSystem -eq "NTFS" -and $_.DriveLetter } |
Select-Object DriveLetter, Label, FileSystem, @{Name="ClusterSizeKB"; Expression={ $_.AllocationUnitSize / 1KB }} |
Format-Table -AutoSize
- Datenträger in Windows auf eine bestimmte Clustergröße formatieren
Wenn man in Windows VMs separate virtuelle HDDs verwendet und diese auf eine bestimmte Clustergröße formatieren möchte, sollte man dies über die CMD durchführen (die GUI der Datenträgerverwaltung bietet dieses Option nicht):
format X: /FS:NTFS /A:64K /Q /V:Label
Erklärung:
- X: = Laufwerksbuchstabe
- /FS:NTFS = Dateisystem NTFS
- /A:64K = Clustergröße 64K
- /Q = Schnellformatierung
- /V:Label = Volumebzeichnung (Label ersetzen)




