Introduction
If you've been running virtual machines with plain KVM and libvirt — maybe through virt-manager, Cockpit, or just raw virsh commands on a Debian or Ubuntu box — you already know the drill. It works. It's just a lot of typing, and there's no web interface holding your hand when something goes wrong at 1 a.m.
Proxmox VE sits on top of the exact same KVM/QEMU stack you're already using, which is the good news: you're not switching hypervisors, you're switching management layers. The disks your libvirt VMs are running on right now — those qcow2 files — are the same format Proxmox uses natively. That means moving a VM over doesn't require a conversion tool or a export wizard. It mostly comes down to copying a file and telling Proxmox where it is.
This guide walks through moving a libvirt-managed VM to a Proxmox VE host: pulling the specs out of libvirt, shutting the guest down cleanly, moving the disk, and wiring everything back up so it boots. I'll also cover the couple of places this trips people up, because it isn't quite as plug-and-play as the shared disk format makes it sound.
What You Will Learn
- How to pull a VM's hardware specs and disk location out of libvirt with
virsh - How to shut a guest down without corrupting its disk image
- How to create a matching VM shell in Proxmox VE and import the qcow2 disk into it
- How to fix the boot and networking issues that show up right after migration
- What to check before you delete the original VM
What Is This Feature?
There isn't a dedicated "import from libvirt" button in Proxmox VE, and you don't need one. Libvirt is a management API that sits in front of KVM/QEMU — it's what virt-manager and Cockpit talk to behind the scenes. Proxmox VE is also a management layer in front of KVM/QEMU, just a different one, with its own CLI (qm) and web GUI. Same hypervisor underneath, two different front ends.
Because of that, a libvirt VM's disk is usually already a qcow2 file — QEMU's native copy-on-write image format, the same one Proxmox VE uses by default for file-based storage. So "migrating" a KVM VM to Proxmox VE is really just: copy that qcow2 file to the Proxmox host, and register it with a new VM using qm disk import (also called qm importdisk — they're the same command). No conversion step, no intermediate format, no virt-v2v needed for a same-hypervisor move like this one.
Why Would You Use It?
Raw libvirt is fine right up until you need something Proxmox does out of the box: scheduled backups you can actually restore from, snapshots you can roll back from the GUI, live migration between hosts, or a permissions system you can hand to someone else without giving them root. I've seen plenty of homelabs running solid on virsh for years — but the moment someone wants a second node, or a backup job that runs itself, they end up here anyway.
It's also common if you're consolidating: you inherited a KVM server from someone else, or you're standardizing a small business's infrastructure on one platform instead of a mix of bare virsh hosts and whatever else accumulated over time. Moving the VMs over beats rebuilding them from scratch, especially if reinstalling means chasing down old application configs nobody wrote down.
Prerequisites
Before you start, make sure you have:
- A working Proxmox VE installation (8.x or 9.x) with enough free storage to hold the imported disk, plus some headroom for the copy itself
- Root or sudo access on both the source libvirt host and the Proxmox VE host
- SSH access between the two machines, or a way to move a file between them (USB drive, shared NFS mount, whatever you've got)
- The VM you're migrating powered off, or the ability to shut it down during a maintenance window
- A few minutes with
virshto note down the guest's RAM, CPU count, and disk path before you touch anything
You don't need virt-v2v for this. That tool matters when you're converting from VMware, Hyper-V, or Xen and need Proxmox to inject VirtIO drivers into a guest that's never seen them. A KVM guest already runs on QEMU, so there's nothing to convert.
Step-by-Step Tutorial
Step 1: Note down the VM's specs on the source host
List your existing VMs so you've got the exact name libvirt uses:
virsh list --all
Then dump the full XML definition for the one you're moving:
virsh dumpxml your-vm-name
You're looking for three things in that output: the amount of memory (in the <memory> tag, usually in KiB), the vCPU count (<vcpu>), and the path to the disk image, which shows up under <disk> as something like /var/lib/libvirt/images/your-vm-name.qcow2. Write these down — you'll need them to build a matching VM on the Proxmox side.
Step 2: Shut the guest down cleanly
Don't copy a disk that's still being written to — you'll end up with a corrupted image. Shut the guest down properly from inside the OS if you can, or use:
virsh shutdown your-vm-name
Confirm it's actually off before moving on:
virsh list --all
It should show shut off, not running. If the guest won't shut down cleanly (it happens), virsh destroy your-vm-name forces it off — but treat that as a last resort, not a habit.
Step 3: Copy the disk to the Proxmox host
With the VM off, copy the qcow2 file over. scp is the simplest option for a one-off move:
scp /var/lib/libvirt/images/your-vm-name.qcow2 root@proxmox-host:/var/lib/vz/template/import/
For a disk over 20-30 GB, rsync is worth using instead — it resumes if the connection drops, which scp won't do:
rsync -avP /var/lib/libvirt/images/your-vm-name.qcow2 root@proxmox-host:/var/lib/vz/template/import/
How long this takes depends entirely on your disk size and network speed. A 40 GB image over a gigabit LAN usually lands somewhere around ten to fifteen minutes; over Wi-Fi, budget a lot more.
Step 4: Create the VM shell in Proxmox
On the Proxmox host, create a new VM using the specs you noted in Step 1 — but don't attach a disk yet:
qm create 150 --name your-vm-name --memory 4096 --cores 2 \
--net0 virtio,bridge=vmbr0 --ostype l26 --scsihw virtio-scsi-single
Pick a VMID that isn't already in use — 150 here is just an example. --ostype l26 tells Proxmox this is a Linux 2.6+ kernel guest, which sets sensible defaults; use win10 or win11 instead if your KVM guest happens to be Windows.
Step 5: Import the disk
Now bring the qcow2 file into the VM's storage:
qm disk import 150 /var/lib/vz/template/import/your-vm-name.qcow2 local-lvm --format qcow2
Swap local-lvm for whatever storage you actually want to use — local-zfs, a Ceph pool, an NFS share, it doesn't matter, as long as it's defined in Proxmox already. This creates an "unused disk" attached to VMID 150 but doesn't wire it into the VM's boot config yet — that's the next step.
Step 6: Attach the disk and set boot order
Open the VM in the Proxmox web GUI, click the Hardware tab, and you'll see the imported disk listed as Unused Disk 0. Double-click it, choose SCSI as the bus, and confirm. Or do it from the command line:
qm set 150 --scsi0 local-lvm:vm-150-disk-0,discard=on,ssd=1
Then set the boot order so it actually starts from that disk:
qm set 150 --boot order=scsi0
SCSI with the VirtIO SCSI single controller (which you already set in Step 4) is the fastest option for a Linux guest and supports discard/TRIM, which matters if you're on SSD-backed storage.
Step 7: Boot it and check the console
qm start 150
Open the console from the GUI (the >_ Console button) and watch it boot. Most Linux distributions come up without any fuss because grub doesn't care what disk controller it's talking to. If it drops to a grub rescue> prompt instead, see the Troubleshooting section below — it's a known snag, not a sign something's broken beyond repair.
Step 8: Install the QEMU Guest Agent
Once you're logged into the migrated guest, install the guest agent so Proxmox can talk to it properly — clean shutdowns, accurate IP reporting in the GUI, that sort of thing:
apt update && apt install qemu-guest-agent
systemctl enable --now qemu-guest-agent
Then in the Proxmox GUI, go to the VM's Options tab and enable QEMU Guest Agent if it isn't already on, and reboot the guest once so the setting takes effect.
Commands Explained
| Command | What it does |
|---|---|
virsh dumpxml <name> | Prints the full libvirt configuration for a VM, including RAM, vCPU count, and disk paths |
virsh shutdown <name> | Sends a clean ACPI shutdown request to the guest OS |
qm create | Creates a new, empty VM definition in Proxmox with the settings you specify |
qm disk import | Imports an external disk image (qcow2, raw, or vmdk) into a Proxmox storage location and attaches it to a VM as an unused disk |
qm set | Changes configuration on an existing VM — attaching a disk, setting boot order, adjusting hardware |
qm start | Boots the VM |
Common Errors
"unable to create image: rename failed" or a permissions error during import usually means the target storage doesn't have room, or the storage type you picked doesn't support the format you're importing. Check available space with pvesm status and confirm the storage accepts disk images (not every storage type does — an NFS share set up only for ISO/backup content won't).
Guest boots to a black screen or hangs at "Booting from Hard Disk" — this is almost always the boot order, not a broken disk. Double-check Step 6; if --boot order=scsi0 didn't get set, the VM has no idea where to boot from.
Guest has no network after boot — libvirt guests are often configured with static rules tied to their old interface name (eth0, or a udev-generated name like ens3). Under Proxmox with a VirtIO NIC, that interface may show up with a different name. Check with ip a inside the guest and update /etc/network/interfaces or your distro's netplan/NetworkManager config to match.
Troubleshooting
The most common real problem is a grub rescue> prompt after the first boot. It happens when grub's configuration references the old disk by a device path that no longer matches — less common with modern distros that boot by UUID, but it still turns up on older installs. Boot a Debian or Ubuntu live ISO attached to the VM, mount the root filesystem, chroot into it, and reinstall grub:
mount /dev/sda1 /mnt
chroot /mnt
update-grub
grub-install /dev/sda
Adjust the device name to match whatever the live environment sees — it may be /dev/sda, /dev/vda, or something else depending on the bus you picked.
If the guest boots but everything feels slow, double check you're actually using the VirtIO SCSI controller and not something libvirt defaulted the disk config to when it was first created (IDE is a common leftover). Slow disk I/O on a migrated VM is almost always a bus mismatch, not a hardware problem.
One more thing worth checking: if the guest was running an older kernel that didn't have VirtIO drivers built in as modules, it may fail to find its root disk at all. This is rare on modern Debian, Ubuntu, or Rocky Linux installs — VirtIO support has been in the mainline kernel for over a decade — but it can bite older or heavily stripped-down images. If you hit it, attaching the disk as SATA temporarily gets you booted so you can install the right kernel modules, then you can switch back to SCSI/VirtIO.
Best Practices
Don't delete the original VM or its disk until you've confirmed the migrated one boots, keeps its data, and holds up under normal use for a day or two. Keep the source qcow2 file around as a fallback — storage is cheap, redoing a migration from scratch is not.
Take a snapshot of the freshly migrated VM before you make any other changes to it. That way, if a driver update or a config tweak goes sideways, you've got a clean rollback point that isn't the original migration.
Run a test backup once the VM is settled in — vzdump 150 from the shell, or through the GUI. It confirms the disk is registered correctly and gives you a real restore point, not just a hope that everything's fine.
If you're migrating more than a couple of VMs, script the repetitive parts — the virsh dumpxml parsing and the scp transfer — rather than doing it by hand each time. It's tedious work to repeat manually, and it's exactly where copy-paste mistakes creep in.
Frequently Asked Questions
Do I need virt-v2v for this?
No. virt-v2v exists to convert VMs from other hypervisors (VMware, Hyper-V, Xen) and inject the drivers they're missing. A KVM/libvirt guest is already running on QEMU, so there's nothing to convert — a straight disk copy and import is enough.
Can I migrate a VM without shutting it down?
Not safely with this method. Copying a qcow2 file while it's being written to will give you a corrupted disk. If you truly can't take downtime, look into setting up storage replication or a proper migration tool built for live moves — that's a different project from a one-time import.
Will my snapshots carry over from libvirt?
No. Libvirt snapshots are stored separately from the base disk and don't transfer with a plain qcow2 copy. If you need the VM's current state preserved, merge or flatten any snapshots on the source side first (virsh blockcommit), so you're copying a single clean disk image.
Does this work for Windows guests running under KVM?
Yes, the disk import process is identical. The one extra step is making sure VirtIO drivers are installed inside the guest before you boot it under a VirtIO-attached disk in Proxmox — if they were already installed under libvirt (which is common, since libvirt also uses VirtIO), you're fine. If not, attach as SATA first, install the drivers from the VirtIO ISO, then switch the bus type.
What if my source and Proxmox host use different storage backends?
qm disk import handles the conversion at import time — you can import a qcow2 file into ZFS, LVM-thin, Ceph, or a directory-based storage, and Proxmox stores it in whatever native format that backend uses. You don't need to convert the file yourself first.
Conclusion
Moving a VM off raw libvirt and onto Proxmox VE is one of the more forgiving migrations you'll do, mostly because you're not actually changing hypervisors — just the layer managing them. Pull the specs, shut the guest down, copy the disk, import it, and fix the handful of boot and network quirks that show up along the way. Once it's running, you pick up everything Proxmox does that plain virsh never did: scheduled backups, snapshots from the GUI, and a much easier path to adding a second node down the road.