Faster VM moves between two nodes with node-local ZFS pools (per-blocksize storages)

Q-wulf

Renowned Member
Mar 3, 2013
616
40
93
my test location
SETUP
- 2 nodes, PVE 9.2.3 (kernel 7.0.14-4-pve, EFI). No shared storage.
- Cluster history: originally built on PVE 7, upgraded through to PVE 9
(so some storage naming/layout predates current versions).
- Each node has its own local ZFS RAID10 pool (4x mirror, WD SN640 NVMe).
- Dedicated migration net: isolated, non-routed, 100 GbE (ConnectX-5), MTU 9000.
- datacenter.cfg: migration: type=insecure,network=<isolated /24>

STORAGE LAYOUT (the core issue)
Each pool is named after its node and split into datasets by volblocksize,
each exposed as its own PVE storage:

PRX01: PRX01-ZFSRaid10/BlockSize32K (32K), /BlockSize64K (64K)
PRX02: PRX02-ZFSRaid10/BlockSize32K (32K), /BlockSize64K (64K)

PVE storage IDs (ZFS, node-restricted):
BS16K/BS32K/BS64K-PRX01ZFSRaid10 (PRX01 only)
BS16K/BS32K/BS64K-PRX02ZFSRaid10 (PRX02 only)
BS512-PRX02ZFS10 (PRX02 only) [benchmarking only]

Structure is symmetric and blocksizes match tier-for-tier, BUT the pool names
differ (PRX01-ZFSRaid10 vs PRX02-ZFSRaid10) and every storage ID is node-scoped.

WORKLOAD (why the tiering exists)
Mixed VMs on both nodes: Windows/Debian/Ubuntu; file servers and SQL/DB servers.
File servers split into large-sequential-video vs. thousands-of-small-files.
Disks are placed on the blocksize tier that suits each pattern — I'd rather not
collapse to a single blocksize.

WHAT I WANT
Faster moves for large VMs. Live migration works but sits at ~145 MB/s.

WHAT'S BLOCKED / WHAT WORKS
1) Offline migration: fails — source storage IDs (BS*-PRX02...) don't exist on
PRX01, so target storage is "not available".
2) Replication (pvesr): needs matching storage ID + dataset path on both nodes;
node-scoped IDs and differing pool names mean no matching target.
3) Works today: live migration with per-disk remap
(BS64K-PRX02 -> BS64K-PRX01, BS32K-PRX02 -> BS32K-PRX01). This is the
~145 MB/s drive-mirror path but preserves tiers.
4) Considering: PBS backup/restore, then move the large 64K disk to its tier
manually.

ALREADY RULED OUT (why 145 MB/s isn't network/disk/CPU/crypto)
Instrumented a full live migration:
- Net ~1.1% ifutil; iperf3 single ~6.9 / 8-parallel ~44 Gbit, ~0 retransmits.
- Jumbo verified (ping -M do -s 8972, 0% loss).
- Both pools idle w/ headroom (zpool iostat), dest shows normal txg sawtooth.
- CPU ~84% idle; top -H shows only guest vCPU threads busy, no pinned migration thread.
- Encryption off (type=insecure).
=> ~145 MB/s is the inherent latency-bound QEMU drive-mirror loop. Acceptable,
but I want delta-only moves for big VMs.

QUESTIONS
1) With node-local pools whose names differ per node, what's the recommended way
to enable pvesr replication? Does it require identical storage IDs AND identical
dataset paths on both nodes?
2) Is the fix to define ONE shared storage ID per tier (both nodes) resolving to
each node's local dataset? If so, how do people handle the differing pool names
— rename a pool, or can the "pool" field differ per node under one ID?
3) Is preserving multiple blocksize tiers across replication sensible, or do people
collapse to one pool/blocksize per node?
4) For a mixed-disk VM (some 32K, some 64K), can each disk keep its matching-blocksize
target, or must all disks land in one storage?
5) Is replication pre-seed the only real lever here, or is there a supported knob
I'm missing for the drive-mirror ceiling?
6) PBS restore: can I place each disk on a chosen target storage on restore, or does
it restore to one storage and I move disk-by-disk afterward? Does stopped-VM
"Move disk" beat the live mirror path or use the same mechanism?
7) Any cases where one method is clearly better given the workload mix — e.g. offline
backup/restore for small-files servers (per-op latency hurts), live mirror fine for
big-video servers?


sidenote: This system was never designed to migrate between hosts. That has now become a requierment
 
PVEVERSION:
Code:
proxmox-ve: 9.2.0 (running kernel: 7.0.14-4-pve)
pve-manager: 9.2.4 (running version: 9.2.4/5e5ae681198514d4)
proxmox-kernel-helper: 9.2.0
pve-kernel-5.15: 7.4-13
pve-kernel-5.13: 7.1-9
proxmox-kernel-7.0.14-4-pve-signed: 7.0.14-4
proxmox-kernel-7.0: 7.0.14-4
proxmox-kernel-6.8.12-33-pve-signed: 6.8.12-33
proxmox-kernel-6.8: 6.8.12-33
pve-kernel-5.15.152-1-pve: 5.15.152-1
pve-kernel-5.13.19-6-pve: 5.13.19-15
pve-kernel-5.13.19-2-pve: 5.13.19-4
amd64-microcode: 3.20251202.1~bpo13+1
ceph-fuse: 19.2.3-pve1
corosync: 3.1.10-pve2
criu: 4.1.1-1
frr-pythontools: 10.6.1-1+pve2
ifupdown2: 3.3.0-1+pmx12
ksm-control-daemon: 1.5-1
libjs-extjs: 7.0.0-6
libproxmox-acme-perl: 1.7.1
libproxmox-backup-qemu0: 2.0.2
libproxmox-rs-perl: 0.4.1
libpve-access-control: 9.1.1
libpve-apiclient-perl: 3.4.2
libpve-cluster-api-perl: 9.1.6
libpve-cluster-perl: 9.1.6
libpve-common-perl: 9.1.16
libpve-guest-common-perl: 6.0.4
libpve-http-server-perl: 6.0.5
libpve-network-perl: 1.6.6
libpve-notify-perl: 9.1.6
libpve-rs-perl: 0.15.3
libpve-storage-perl: 9.1.6
libspice-server1: 0.15.2-1+b1
lvm2: 2.03.31-2+pmx1
lxc-pve: 7.0.0-2
lxcfs: 7.0.0-pve1
novnc-pve: 1.7.0-2
openvswitch-switch: 3.5.0-1+b1
proxmox-backup-client: 4.2.2-1
proxmox-backup-file-restore: 4.2.2-1
proxmox-backup-restore-image: 1.0.0
proxmox-firewall: 1.2.3
proxmox-kernel-helper: 9.2.0
proxmox-mail-forward: 1.0.3
proxmox-mini-journalreader: 1.7
proxmox-offline-mirror-helper: 0.7.4
proxmox-widget-toolkit: 5.2.6
pve-cluster: 9.1.6
pve-container: 6.1.10
pve-docs: 9.2.3
pve-edk2-firmware: 4.2025.05-2
pve-esxi-import-tools: 1.0.1
pve-firewall: 6.0.4
pve-firmware: 3.18-4
pve-ha-manager: 5.2.4
pve-i18n: 3.9.0
pve-qemu-kvm: 11.0.2-1
pve-xtermjs: 6.0.0-2
qemu-server: 9.1.18
smartmontools: 7.5-pve2
spiceterm: 3.4.2
swtpm: 0.8.0+pve3
vncterm: 1.9.2
zfsutils-linux: 2.4.3-pve1



cat /etc/pve/storage.cfg
Code:
dir: local
        path /var/lib/vz
        content iso,vztmpl,backup

zfspool: local-zfs
        pool rpool/data
        content rootdir,images
        sparse 1

cifs: ISO_OEVQ-NAS-01
        path /mnt/pve/ISO_OEVQ-NAS-01
        server 10.61.5.31
        share PRX-ISO
        content vztmpl,iso
        domain OEVQ-NAS-01
        nodes PRX03,PRX02,PRX01
        prune-backups keep-all=1
        username Proxmox-ISO-Access

zfspool: BS16K-PRX01ZFSRaid10
        pool PRX01-ZFSRaid10/BlockSize16K
        blocksize 16K
        content images,rootdir
        mountpoint /PRX01-ZFSRaid10/BlockSize16K
        nodes PRX01
        sparse 1

zfspool: BS32K-PRX01ZFSRaid10
        pool PRX01-ZFSRaid10/BlockSize32K
        blocksize 32K
        content images,rootdir
        mountpoint /PRX01-ZFSRaid10/BlockSize32K
        nodes PRX01
        sparse 1

zfspool: BS64K-PRX01ZFSRaid10
        pool PRX01-ZFSRaid10/BlockSize64K
        blocksize 64K
        content rootdir,images
        mountpoint /PRX01-ZFSRaid10/BlockSize64K
        nodes PRX01
        sparse 1

zfspool: BS16K-PRX02ZFSRaid10
        pool PRX02-ZFSRaid10/BlockSize16K
        blocksize 16K
        content rootdir,images
        mountpoint /PRX02-ZFSRaid10/BlockSize16K
        nodes PRX02
        sparse 1

zfspool: BS32K-PRX02ZFSRaid10
        pool PRX02-ZFSRaid10/BlockSize32K
        blocksize 32K
        content rootdir,images
        mountpoint /PRX02-ZFSRaid10/BlockSize32K
        nodes PRX02
        sparse 1

zfspool: BS64K-PRX02ZFSRaid10
        pool PRX02-ZFSRaid10/BlockSize64K
        blocksize 64K
        content images,rootdir
        mountpoint /PRX02-ZFSRaid10/BlockSize64K
        nodes PRX02
        sparse 1

pbs: PBS-PRX03
        datastore PRX03-ZFSRaidZ1
        server 10.61.4.103
        content backup
        fingerprint a7:43:a6:76:50:6d:3e:c3:30:de:ef:59:f6:2c:ad:29:1a:7c:a3:c3:10:ad:bc:28:ee:33:ed:df:08:90:2b:ec
        nodes PRX03,PRX01,PRX02
        prune-backups keep-all=1
        username OEVQ_PBSBackupJobs@pbs

zfspool: BS512-PRX02ZFS10
        pool PRX02-ZFSRaid10
        blocksize 512
        content images,rootdir
        mountpoint /PRX02-ZFSRaid10
        nodes PRX02
        sparse 1



cat /etc/pve/datacenter.cfg

Code:
console: html5
keyboard: de
migration: type=insecure,network=10.61.4.0/24
next-id: lower=1000,upper=3000
replication: secure,network=10.61.4.101/24

Note: This is a dedicated 100G link between PRX1, PRX2 and PRX2


zpool status
Code:
pool: PRX02-ZFSRaid10
 state: ONLINE
status: Some supported and requested features are not enabled on the pool.
        The pool can still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
        the pool may no longer be accessible by software that does not support
        the features. See zpool-features(7) for details.
  scan: scrub repaired 0B in 00:10:55 with 0 errors on Sun Jul 12 00:34:56 2026
config:


        NAME                               STATE     READ WRITE CKSUM
        PRX02-ZFSRaid10                    ONLINE       0     0     0
          mirror-0                         ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A0672925  ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A067F85D  ONLINE       0     0     0
          mirror-1                         ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A057469B  ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A05D5703  ONLINE       0     0     0
          mirror-2                         ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A06722FF  ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A05D56DD  ONLINE       0     0     0
          mirror-3                         ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A0676439  ONLINE       0     0     0
            nvme-WUS4BB038D7P3E3_A067256D  ONLINE       0     0     0


errors: No known data errors


  pool: rpool
 state: ONLINE
status: Some supported and requested features are not enabled on the pool.
        The pool can still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
        the pool may no longer be accessible by software that does not support
        the features. See zpool-features(7) for details.
  scan: scrub repaired 0B in 00:01:40 with 0 errors on Sun Jul 12 00:25:42 2026
config:


        NAME                                                  STATE     READ WRITE CKSUM
        rpool                                                 ONLINE       0     0     0
          mirror-0                                            ONLINE       0     0     0
            ata-INTEL_SSDSC2KG240G8_BTYG0265001Q240AGN-part3  ONLINE       0     0     0
            ata-INTEL_SSDSC2KG240G8_BTYG026402JG240AGN-part3  ONLINE       0     0     0


errors: No known data errors
 
QUESTIONS
1) With node-local pools whose names differ per node, what's the recommended way
to enable pvesr replication? Does it require identical storage IDs AND identical
dataset paths on both nodes?
2) Is the fix to define ONE shared storage ID per tier (both nodes) resolving to
each node's local dataset? If so, how do people handle the differing pool names
— rename a pool, or can the "pool" field differ per node under one ID?
3) Is preserving multiple blocksize tiers across replication sensible, or do people
collapse to one pool/blocksize per node?
4) For a mixed-disk VM (some 32K, some 64K), can each disk keep its matching-blocksize
target, or must all disks land in one storage?
5) Is replication pre-seed the only real lever here, or is there a supported knob
I'm missing for the drive-mirror ceiling?
6) PBS restore: can I place each disk on a chosen target storage on restore, or does
it restore to one storage and I move disk-by-disk afterward? Does stopped-VM
"Move disk" beat the live mirror path or use the same mechanism?
7) Any cases where one method is clearly better given the workload mix — e.g. offline
backup/restore for small-files servers (per-op latency hurts), live mirror fine for
big-video servers?
 
Hi,

I still don't get an idea to have different names for different nodes. To me it doesn't add a thing to your tiering except for unnecessary complication. You write you need different blocksizes, well, that makes sense, but different names per node? Just omit those 01, 02 and Bob's your uncle