Moving a VM between hypervisors is usually a matter of converting the disk image format. VirtualBox writes to VDI, KVM and libvirt prefer qcow2, VMware uses VMDK, and Hyper-V uses VHD/VHDX. The qemu-img tool from the QEMU project reads and writes all of them, so a VDI-to-qcow2 conversion is a one-line command once you know the flags.
This guide covers the whole workflow on a Rocky Linux 10 host: install qemu-img, inspect a source VDI, convert it to qcow2 with and without compression, and import the result into libvirt. The same commands work on AlmaLinux 10, RHEL 10, Fedora 42, Debian 13, and Ubuntu 24.04 with the only difference being the package manager.
Tested April 2026 on Rocky Linux 10.1 with qemu-img 10.0.0 from the QEMU 10.0 release packaged as qemu-kvm-10.0.0-14.el10_1.5
Why convert VDI to qcow2
qcow2 is the native format for QEMU and libvirt. It supports thin provisioning, compression, encryption, and live snapshots. VDI supports snapshots too but most Linux virtualization stacks expect qcow2. Converting once gives you a file that boots under KVM, Proxmox, OpenStack, and oVirt without further translation. The conversion is a deep copy (not a pointer or link) so the destination file is fully independent and can live on a different filesystem or host, including a BtrFS subvolume for snapshotting.
Step 1: Install qemu-img
On Rocky Linux 10, AlmaLinux 10, and RHEL 10 the tool lives in the qemu-img package in BaseOS:
sudo dnf install -y qemu-img
On Debian 13 or Ubuntu 24.04, install it with apt:
sudo apt update
sudo apt install -y qemu-utils
Confirm the version:
qemu-img --version | head -1
You should see QEMU 10.x on a current distro:
qemu-img version 10.0.0 (qemu-kvm-10.0.0-14.el10_1.5)
Step 2: Inspect the source VDI
Before converting, always inspect the source file with qemu-img info. You’ll see the format, the virtual size, the actual on-disk size (thin-provisioning footprint), and any snapshots or encryption:
qemu-img info /path/to/source-disk.vdi
Typical output for a VDI that hasn’t been filled yet:
image: /tmp/test-disk.vdi
file format: vdi
virtual size: 256 MiB (268435456 bytes)
disk size: 4 KiB
cluster_size: 1048576
Child node '/file':
filename: /tmp/test-disk.vdi
protocol type: file
file length: 1.5 KiB (1536 bytes)
disk size: 4 KiB
Format specific information:
extent size hint: 1048576
Two important numbers: virtual size is what the guest sees, disk size is what the file actually occupies on the host filesystem. If the VDI has snapshots listed, flatten them inside VirtualBox before converting (Machine → Snapshots → Delete) so you end up with a single consistent current state.
Step 3: Convert VDI to qcow2
The basic syntax is qemu-img convert -O <target-format> <source> <dest>. Add -p to show a progress bar, which matters when the source is 20 GB or larger:
qemu-img convert -p -O qcow2 /path/to/source-disk.vdi /path/to/dest-disk.qcow2
The progress bar runs from 0 to 100 and then the command exits without further output on success. Verify the destination with another qemu-img info call:
qemu-img info /path/to/dest-disk.qcow2
The virtual size should match the source, and the format should now say qcow2:
image: /tmp/test-disk.qcow2
file format: qcow2
virtual size: 256 MiB (268435456 bytes)
disk size: 196 KiB
cluster_size: 65536
Format specific information:
compat: 1.1
compression type: zlib
lazy refcounts: false
refcount bits: 16
corrupt: false
extended l2: false
Step 4: Convert with compression
For images you plan to archive or ship over a network, add -c to enable zlib compression in the qcow2 output. Reads pay a small CPU overhead, writes are not allowed on compressed qcow2 clusters (the file becomes effectively read-only for modifications), so this is best reserved for golden images or snapshots:
qemu-img convert -p -c -O qcow2 /path/to/source-disk.vdi /path/to/dest-disk-compressed.qcow2
On a real Windows 11 VDI around 15 GB filled, compression typically cuts the on-disk footprint by 40-60%. For already-compressed content (video, encrypted files) the savings are closer to zero.
Step 5: Import the result into libvirt
Move the new qcow2 file into the libvirt default storage pool and refresh the pool so libvirt sees it:
sudo mv /path/to/dest-disk.qcow2 /var/lib/libvirt/images/
sudo chown qemu:qemu /var/lib/libvirt/images/dest-disk.qcow2
sudo restorecon -v /var/lib/libvirt/images/dest-disk.qcow2
sudo virsh pool-refresh default
The restorecon call puts the correct SELinux label on the file so libvirt_t can read it. Without it, SELinux on a Rocky 10 box will deny the VM from opening the disk at boot. For the full SELinux context background see our SELinux troubleshooting guide for Rocky 10.
Create a new VM that uses this disk as its boot volume:
sudo virt-install --name imported-vm \
--ram 4096 --vcpus 2 \
--disk path=/var/lib/libvirt/images/dest-disk.qcow2,format=qcow2 \
--os-variant detect=on \
--import --noautoconsole
If the guest fails to boot with a kernel panic about not finding the root device, the issue is almost always that the guest’s /etc/fstab uses labels or UUIDs that changed when the disk was cloned. Boot a rescue ISO, chroot in, fix fstab, and rebuild the initramfs.
Going the other way: qcow2 to VDI
The same tool converts back. The only difference is the -O flag:
qemu-img convert -p -O vdi /path/to/source.qcow2 /path/to/dest.vdi
VDI is the native VirtualBox format, so this is the right direction if you’re moving a Linux VM off KVM back to a developer workstation running VirtualBox. The resulting VDI won’t have snapshot history (qemu-img flattens the image), which is exactly what you want for a one-off import.
Related format conversions
qemu-img handles every common VM disk format. A few other useful target formats:
- VMDK:
-O vmdkfor VMware Workstation and ESXi - VHD/VHDX:
-O vpcor-O vhdxfor Hyper-V - RAW:
-O rawfor Proxmox LVM-thin or dd-style backup
For more advanced operations including creating, resizing, and committing snapshots, see our dedicated guide on managing VM images in KVM with qemu-img. If you’re building a Rocky 10 KVM host from scratch, start with our Rocky 10 post-install tips to get the base server ready, then follow our firewalld configuration guide before exposing any VNC or migration ports on the network.