Skip to content
Infrastructure

Moving Proxmox VM disks between storage pools

By Victor Da Luz
proxmox zfs storage homelab lvm
Moving Proxmox VM disks between storage pools

There are two situations that push you to move a Proxmox VE VM’s disks to a different storage pool. The first: you want to enable ZFS replication across cluster nodes, but the VM is sitting on LVM-thin storage, which doesn’t support it. The second: a pool fills up and something else on that pool is suffering for it. In both cases, qm move-disk handles the migration while the VM keeps running.

This is how the command works, how to run it safely, and two gotchas that showed up after I’d already moved the disks.

How qm move-disk works

Proxmox uses QEMU’s drive-mirror block job under the hood. While the VM runs, it continuously mirrors the disk to the target storage. When the mirror catches up to 100%, Proxmox pauses writes briefly and commits the final delta. The VM doesn’t shut down; there are a few seconds of I/O pause per disk at the end, not a full restart.

Before you start

Three things to check:

  1. Back up the VM first. vzdump <vmid> --storage <backup-storage> --mode snapshot. The move is generally safe, but you want a recovery point.
  2. Confirm the target pool has enough space. zpool list <pool>. Available should exceed the disk size being moved.
  3. Check for active backup jobs. If Proxmox Backup Server is running a job for the same VM, the move can conflict. pvesh get /cluster/tasks shows what’s running.

Moving the disks

Start by listing the current disk layout:

qm config <vmid>

Note the disk identifiers (scsi0, efidisk0, etc.) and which storage pool they’re on. Then move one disk at a time:

qm move-disk <vmid> scsi0    pve-containers --delete 1
qm move-disk <vmid> efidisk0 pve-containers --delete 1

--delete 1 removes the source volume after a successful copy. If you’d rather be conservative, omit it on the first run and delete the orphaned volume manually once you’ve verified the VM is healthy.

Verify after all moves:

qm config <vmid> | grep -E 'scsi|virtio|efidisk'
qm status <vmid>

All disk references should show the target pool. The VM should still be running.

Rough timing (NVMe to SATA SSD):

  • 32GB OS disk: ~10 minutes
  • 8-9GB data disk: ~14 seconds (mostly thin-allocated)
  • 128KB EFI disk: instant

Gotcha 1: the stale local-lvm definition

Once you’ve moved everything off local-lvm, the storage definition in Proxmox still exists. It’s cluster-wide config in /etc/pve/storage.cfg, so it applies to all nodes. If those nodes no longer have the underlying LVM thinpool, pvesm status will start reporting errors on every check:

no such logical volume pve/data

The fix is to verify nothing still references local-lvm, then remove the definition:

# Check no VM or CT configs still reference it
grep -r "local-lvm" /etc/pve/nodes/

# If nothing found, remove the storage
pvesm remove local-lvm

Run pvesm status again to confirm the errors are gone.

Gotcha 2: unused disk entries break replication

After a disk move, Proxmox sometimes leaves an unusedN entry in the VM’s config. These point at the old storage location - a dataset that no longer exists after you passed --delete 1. If you then set up ZFS replication, the job fails on the first run:

zfs error: cannot open 'pool/vm-<vmid>-disk-N': dataset does not exist

Proxmox tries to replicate every disk in the config, including unusedN entries. With a dangling reference, the replication job fails every cycle until you remove the entry. It won’t fix itself.

Find and clean up dangling entries before setting up replication:

# Check for unused entries
qm config <vmid> | grep unused

# Remove each one
qm set <vmid> --delete unused0

Replication setup

With the disks on ZFS, create a replication job through Datacenter → Replication → Add in the Proxmox UI, or via CLI:

pvesr create-local-job <vmid>-0 <target-node> --schedule '*/15'

The first run is a full sync. Subsequent runs are incremental. Fifteen-minute intervals work well for most homelab VMs.

What I would do differently

  • Check for unused disk entries before setting up replication. Running qm config <vmid> | grep unused takes two seconds and avoids a replication job that fails every fifteen minutes.
  • Remove the stale local-lvm definition the same day. Easy to defer and forget until monitoring starts alerting on it at 2am.
  • Verify the VM between each disk move. qm status <vmid> is quick and gives early warning if something went sideways mid-copy.

Related reading

Consolidating audiobooks and ebooks into a single Audiobookshelf
Infrastructure

Consolidating audiobooks and ebooks into a single Audiobookshelf

I was running two media servers, Audiobookshelf for audiobooks and Kavita for ebooks, when one could do both. Rebuilding the homelab in v3 was the excuse to merge them: one Ansible-deployed Audiobookshelf, local-disk storage, and a USB-drive ZFS scare in the middle of the migration.

Read

Ready to Transform Your Career?

Let's work together to unlock your potential and achieve your professional goals.