← Back to Blog

How to Convert VMDK to QCOW2 with qemu-img: Sparse, Flat, Split, Snapshot and OVA Disks

Published · by Leo Gashi, RS Computers

KVM VMware qemu-img Migration

To convert VMDK to QCOW2, install qemu-img and run one command: qemu-img convert -p -f vmdk -O qcow2 disk.vmdk disk.qcow2. Point it at the small descriptor file (or the single .vmdk if there is only one), keep every extent file in the same folder, then run qemu-img check on the result and qemu-img compare against the original. That is the whole conversion. What goes wrong in practice is everything around it: the wrong file passed in, a missing -flat.vmdk, a snapshot layer instead of the real disk, or a guest that converts perfectly and then cannot find its own boot disk on KVM.

We ran every command on this page on 24 October 2026 on a Debian 13 KVM server with qemu-img 10.0.13, against four kinds of VMDK we built from the same 2 GiB disk: monolithic sparse, streamOptimized (the kind inside an OVA), monolithic flat (what ESXi keeps on a datastore) and a disk split into 2 GB extents (VMware Workstation's "split into multiple files"). All four came out as the same 118 MiB QCOW2, and qemu-img compare reported "Images are identical." every time. If you are moving a whole VMware VM, our VMware to KVM migration guide covers the export, driver and test-boot side; this page is about the disk conversion itself and the errors you meet on the way.

Which VMDK do you have?

"VMDK" is a family of layouts, not one format. qemu-img reads all the common ones, but you have to give it the right file. Look at what is in the folder first:

What you see in the folderVMDK typeWhere it comes fromFile to pass to qemu-img
One disk.vmdk, size close to the data on itmonolithicSparseWorkstation, Fusion, VirtualBoxThat file
One disk.vmdk inside an .ova, smaller than the datastreamOptimized (compressed)OVA and OVF exportsThat file, after unpacking the OVA
disk.vmdk of about 500 bytes plus disk-flat.vmdk as big as the whole diskmonolithicFlatESXi datastore, copied with scpThe small descriptor
disk.vmdk plus disk-s001.vmdk, disk-s002.vmdk and so ontwoGbMaxExtentSparseWorkstation "split into multiple files"The small descriptor
Extra files such as disk-000001.vmdkSnapshot (delta) diskA VM with snapshotsThe newest -00000N.vmdk, or delete the snapshots in VMware first

The descriptor is plain text, so you can simply read it. Ours, for the flat disk:

cat web01-monolithicFlat.vmdk
# Disk DescriptorFile
version=1
CID=8cff3439
parentCID=ffffffff
createType="monolithicFlat"

# Extent description
RW 4194304 FLAT "web01-monolithicFlat-flat.vmdk" 0

createType names the layout, and each RW line is one extent file with its size in 512-byte sectors (4,194,304 sectors is 2 GiB). For a split disk there is one RW ... SPARSE line per -s00N file, and every one of them must sit next to the descriptor. A parentCID other than ffffffff means this is a snapshot layer that depends on another disk. The libvmdk format notes describe each field if you meet one that is not here.

Step 1: install qemu-img

On Debian or Ubuntu

Run as root (or put sudo in front). qemu-img lives in the small qemu-utils package; you do not need the whole QEMU emulator.

apt update
apt install -y qemu-utils
qemu-img --version

Expected output on Debian 13:

qemu-img version 10.0.13 (Debian 1:10.0.13+ds-0+deb13u1)

On AlmaLinux, Rocky or Fedora the package is qemu-img: dnf install -y qemu-img.

On Windows

For Windows, the QEMU download page points to the installers built by Stefan Weil, which include qemu-img.exe. The newest installer in his 64-bit folder when we looked was qemu-w64-setup-20260811.exe. Install it, then in PowerShell:

& "C:\Program Files\qemu\qemu-img.exe" convert -p -f vmdk -O qcow2 D:\vm\web01.vmdk D:\vm\web01.qcow2
& "C:\Program Files\qemu\qemu-img.exe" check D:\vm\web01.qcow2

It is the same program with the same options, so everything below applies. We ran our tests on Linux, not on this Windows build. If the VMware VM itself is on your Windows PC, WSL with Debian and the apt commands above also works, and the Windows drives are under /mnt/c, /mnt/d.

Step 2: look before you convert

Run as your normal user, in the folder with the disk files:

qemu-img info db01.vmdk

Our split disk printed (trimmed):

image: db01.vmdk
file format: vmdk
virtual size: 5 GiB (5368709120 bytes)
disk size: 119 MiB
Format specific information:
    create type: twoGbMaxExtentSparse
    extents:
        [0]:
            filename: db01-s001.vmdk
            format: SPARSE
        [1]:
            filename: db01-s002.vmdk
            format: SPARSE
        [2]:
            filename: db01-s003.vmdk
            format: SPARSE

Three numbers matter. Virtual size is the disk the guest sees, so the server you move to needs at least that much disk. Disk size is the space the files really use. And the extents list tells you every file qemu-img will open; if one is missing, you find out here instead of halfway through a 200 GB copy.

Then ask how much room the result needs:

qemu-img measure -f vmdk -O qcow2 web01-streamOptimized.vmdk
df -h .
required size: 123863040
fully allocated size: 2148073472

The QCOW2 needs about 118 MiB here, not 2 GiB, because only the used parts are copied. Ask the same for -O raw and qemu-img answers with the full virtual size, but on a normal Linux filesystem the RAW file is sparse and uses far less; more on that below.

Step 3: convert VMDK to QCOW2

qemu-img convert -p -f vmdk -O qcow2 web01-monolithicSparse.vmdk web01.qcow2

-p shows a progress percentage, -f vmdk says what goes in and -O qcow2 what comes out. For a flat or split disk, the command is the same with the descriptor as input:

qemu-img convert -p -f vmdk -O qcow2 web01-monolithicFlat.vmdk web01-from-flat.qcow2
qemu-img convert -p -f vmdk -O qcow2 db01.vmdk db01.qcow2

Now check the new file and compare it with the source. Do not delete anything until both pass:

qemu-img check web01.qcow2
qemu-img compare -f vmdk -F qcow2 web01-monolithicSparse.vmdk web01.qcow2
No errors were found on the image.
1881/32768 = 5.74% allocated, 0.00% fragmented, 0.00% compressed clusters
Image end offset: 123731968
Images are identical.

check reads the QCOW2's own bookkeeping; exit code 0 means consistent, 2 means corrupted and 3 means leaked clusters but no corruption (qemu-img documentation). compare reads both disks end to end and is the real proof that the guest will see the same bytes.

Here is what our four VMDKs, all holding the same 2 GiB ext4 disk with 110 MB of files, turned into:

Input VMDKFiles and size on diskQCOW2 resultcheck / compare
monolithicSparse1 file, 118 MiB118 MiBno errors / identical
streamOptimized1 file, 53.9 MiB (compressed)118 MiBno errors / identical
monolithicFlat512-byte descriptor + 2 GiB flat file118 MiBno errors / identical
twoGbMaxExtentSparse (5 GiB disk)descriptor + 3 extent files, 119 MiB118 MiBno errors / identical

One thing to notice: the streamOptimized input was smaller than the QCOW2 output. That is normal. The OVA's disk is compressed, and a plain QCOW2 is not, unless you ask for it.

Compressed, sparse or preallocated output

The same command takes a few more options, and picking the right one saves an upload or a slow disk later.

Compressed QCOW2 for the upload

qemu-img convert -p -c -f vmdk -O qcow2 -o compression_type=zstd web01-streamOptimized.vmdk web01-upload.qcow2

-c compresses every cluster as it is written. QCOW2 knows two compression types, zlib (the default) and zstd. On our disk:

OutputSizeTime on our 3 vCPU test server
QCOW2, no compression118 MiB3.4 to 11.3 s
QCOW2, -c zlib57.2 MiB32.4 s
QCOW2, -c zstd54.7 MiB8.7 s

zstd was nearly four times faster here and slightly smaller. Compression only applies while converting: data the VM writes later is stored uncompressed, which is fine. Check the file before sending it:

qemu-img info web01-upload.qcow2 | grep "compression type"
sha256sum web01-upload.qcow2
    compression type: zstd
1155d9a80e50d8840c64c7cba70c1f855a999ba54490e03ed2a2f2cfe72bac58  web01-upload.qcow2

Older QEMU versions (before 5.1) cannot open a zstd image. If the destination is unknown, plain -c with zlib is the safe choice.

RAW instead of QCOW2

qemu-img convert -p -f vmdk -O raw web01-streamOptimized.vmdk web01.raw
ls -ls web01.raw
126980 -rw-r--r-- 1 root root 2147483648 Oct 24 21:54 web01.raw

The file says 2 GiB, but the first number shows it uses only 124 MiB of real space: the empty parts are holes. Copy such a file with cp --sparse=always or rsync --sparse, or the holes get filled in on the way.

The -S 0 trap

Some guides add -S 0. That turns off the search for empty space, so the output is fully allocated. With it, our RAW file used the full 2 GiB and the conversion took 73.6 seconds instead of 4.3. Leave it out unless you really want a thick disk. If you want space reserved for a QCOW2 on purpose, -o preallocation=falloc did it in 4.4 seconds.

Speed on big disks

-m 16 runs 16 parallel requests instead of the default 8, and -W lets them write out of order. The QEMU documentation recommends -W only for preallocated targets such as block devices, so use it when writing straight to a disk or LVM volume, not to a growing QCOW2 file.

OVA files: unpack with tar first

An OVA is not a disk image. It is an uncompressed tar archive (the DMTF OVF 2.1.1 standard requires the USTAR tar format, with the .ovf descriptor as the first file) holding the descriptor, one VMDK per disk and usually a .mf checksum list. List it, unpack it, verify it:

tar -tvf web01.ova
tar -xvf web01.ova
sed -E 's/^SHA256\((.*)\)= (.*)/\2  \1/' web01.mf | sha256sum -c
-rw-r--r-- root/root       217 2026-10-24 21:56 web01.ovf
-rw-r--r-- root/root  56492032 2026-10-24 21:56 web01-disk1.vmdk
-rw-r--r-- root/root       175 2026-10-24 21:56 web01.mf
web01.ovf
web01-disk1.vmdk
web01.mf
web01.ovf: OK
web01-disk1.vmdk: OK

The OVF standard allows SHA1 or SHA256 in the manifest. If your .mf lines start with SHA1(, change SHA256 to SHA1 in the sed line and use sha1sum -c. Then convert web01-disk1.vmdk exactly as in Step 3. The .ovf is readable XML; search it for VirtualQuantity (CPUs and memory) and firmware to see what the VM had.

Snapshot disks: let qemu-img merge them

A VM with snapshots writes new data to delta files like web01-000001.vmdk, each pointing back to its parent. Convert the base disk and you get the VM as it was when the first snapshot was taken, without anything since. Either delete (consolidate) all snapshots in VMware before exporting, or give qemu-img the newest delta and it follows the chain. We tested the second way:

qemu-img info --backing-chain web01-000001.vmdk | grep -E "^image|backing file"
qemu-img convert -p -f vmdk -O qcow2 web01-000001.vmdk merged.qcow2
image: web01-000001.vmdk
backing file: web01-monolithicSparse.vmdk
image: web01-monolithicSparse.vmdk

The result is one standalone QCOW2 with the whole chain merged into it; qemu-img compare against the chain said "Images are identical."

Errors you will probably meet

Message or symptomCauseFix
Could not open 'web01.vmdk': Could not open 'web01-flat.vmdk': No such file or directoryThe descriptor was copied without its data file, or the data file was renamedCopy the -flat.vmdk (or all -s00N files) into the same folder, with the exact name in the descriptor's RW line
Image is not in qcow2 format (or vmdk)Wrong -f valueRun qemu-img info and use the format it reports
qemu-img info on -flat.vmdk says file format: rawThe flat file is a plain raw disk without the VMDK headerNot an error: convert the descriptor, or convert the flat file with -f raw
Converted disk is missing recent filesThe base disk of a snapshot chain was convertedConvert the newest -00000N.vmdk or consolidate snapshots first
Output much bigger than the data-S 0, or the copy tool filled the holes in a RAW fileDrop -S 0; use QCOW2, or --sparse when copying RAW
Conversion stops with "No space left on device"Target filesystem too smallRun qemu-img measure and df -h before you start

A thick ESXi disk is a special case. If the VM has a 500 GB thick disk with 40 GB of data, scp moves all 500 GB. On the ESXi host, vmkfstools -i web01.vmdk -d thin web01-thin.vmdk makes a thin copy first; that is an ESXi command, so we could not run it on our Linux lab, and an OVF export from the ESXi host client avoids the problem too.

The disk converts, the VM still has to boot

qemu-img copies bytes. It does not change the operating system inside, and on KVM the disk and network card are VirtIO devices instead of VMware's PVSCSI or LSI controller and vmxnet3 card. Most "converted but will not boot" cases are one of the following.

Windows guests: VirtIO drivers before the move

Windows without the VirtIO storage driver stops at boot with INACCESSIBLE_BOOT_DEVICE (0x7B). Install the drivers while the VM still runs on VMware, using virtio-win-guest-tools.exe from the virtio-win ISO, then shut down cleanly with Fast Startup off and convert. Our article on VirtIO drivers and the QEMU guest agent explains what each driver does and what breaks without it. Windows is chosen on our Windows VPS plans as usual; you bring your own licence key.

Linux guests: initramfs

The kernel needs the VirtIO disk driver at boot, before the root filesystem is mounted, so it has to be in the initramfs. Check inside the VM, as root, before you shut it down on VMware:

lsinitramfs /boot/initrd.img-$(uname -r) | grep -E 'virtio_(blk|scsi|net)'

On a Debian 13 KVM server this printed:

usr/lib/modules/6.12.43+deb13-amd64/kernel/drivers/block/virtio_blk.ko.xz
usr/lib/modules/6.12.43+deb13-amd64/kernel/drivers/net/virtio_net.ko.xz
usr/lib/modules/6.12.43+deb13-amd64/kernel/drivers/scsi/virtio_scsi.ko.xz

If nothing prints on Debian or Ubuntu, add the modules and rebuild:

printf 'virtio_pci\nvirtio_blk\nvirtio_scsi\nvirtio_net\n' >> /etc/initramfs-tools/modules
update-initramfs -u -k all

We tested this in a Debian 13 container set to build a minimal initramfs (MODULES=list): before, the grep found 0 VirtIO modules; after the two lines, it listed virtio_blk, virtio_net and virtio_scsi for kernel 6.12.113.

On AlmaLinux, Rocky and RHEL, which build the initramfs only for the hardware they run on:

dracut -f --add-drivers "virtio_blk virtio_scsi virtio_net virtio_pci"
lsinitrd | grep -oE 'virtio_(blk|scsi|net)' | sort -u

In a Rocky Linux 9 container with kernel 5.14.0-687 (where we had to name the kernel with --kver, because a container runs the host's kernel), the check printed virtio_blk, virtio_net and virtio_scsi. Inside a real VM, the two lines above are enough.

Without this, the usual symptom is a dracut emergency shell or "Gave up waiting for root file system device".

Linux guests: fstab with UUIDs

On VMware the disk is often /dev/sda; on KVM it can become /dev/vda. An /etc/fstab line with /dev/sda1 then points at nothing. Use the filesystem UUID instead, which travels with the data. Get it with blkid:

blkid -s UUID -o value /dev/sda1

Put it in fstab as UUID=... in place of /dev/sda1, then let findmnt check the file (it reads /etc/fstab; we pointed it at a test copy with --tab-file). Our test fstab still had a second disk as /dev/sdb1, and it caught it:

findmnt --verify
/data
   [E] unreachable on boot required target: No such file or directory
   [W] unreachable source: /dev/sdb1: No such file or directory

A line like that is what drops a server into emergency mode on its first boot. Fix it with the UUID too, or add nofail to the options of a disk that may not come along. Also note the network card name changes (for example from ens192 to ens3 or eth0), so an interface configured by name needs its config updated, and swap open-vm-tools for qemu-guest-agent.

Frequently asked questions

How do I convert VMDK to QCOW2?

Install qemu-img (package qemu-utils on Debian and Ubuntu) and run qemu-img convert -p -f vmdk -O qcow2 disk.vmdk disk.qcow2. Then run qemu-img check disk.qcow2 and qemu-img compare -f vmdk -F qcow2 disk.vmdk disk.qcow2; "Images are identical." means the conversion is exact.

Which file do I convert when there is a -flat.vmdk?

The small descriptor file without "-flat" in its name. qemu-img reads it and opens the flat file named inside. Both must be in the same folder.

Can I convert VMDK to QCOW2 on Windows?

Yes. Install the Windows build of QEMU from qemu.weilnetz.de and run qemu-img.exe from C:\Program Files\qemu with the same options, or use qemu-utils inside WSL.

Is QCOW2 or RAW better after converting?

QCOW2 for moving the disk around: it stores only used space and can be compressed. RAW is a plain byte copy that is sparse on Linux filesystems but easy to inflate by accident. A KVM host can run either.

Does converting change anything inside the VM?

No. qemu-img copies the disk byte for byte. Drivers, fstab and network names are fixed inside the guest, before or after the conversion.

How long does it take?

It depends on the used data, not the virtual size, and on disk speed. Our 118 MiB of data converted in a few seconds; compressing with zlib took 32 seconds and zstd 9. Expect minutes per tens of gigabytes on NVMe, longer on network storage.

Bring the QCOW2 to a KVM server

Every RS Computers VPS and VDS is a KVM virtual machine with VirtIO disk and network, in Amsterdam, Dublin and Prishtina; VPS plans use shared vCPUs and VDS plans dedicated ones. Pick a plan with at least the virtual size you saw in qemu-img info, in the city closest to your users (Netherlands, Ireland or Kosovo). When the QCOW2 or RAW file passes check and compare, send us the operating system, BIOS or UEFI, the virtual size and the SHA-256 checksum on Telegram or at info@rscomputers-ks.com. We arrange the upload and import the disk onto your server; like other setup work, it is quoted per job. Keep the original VMDK until the new server has run for a week.

← All articles

Chat on Telegram