When working with KVM virtual machines (especially Windows guests), many administrators encounter a frustrating reality: traditional shrinking methods like qemu-img convert -c
often fail to deliver significant space savings. The ModernIE project demonstrates what's theoretically possible - Windows 7 VMs under 10GB - while our own attempts yield 80GB+ images even after extensive optimization.
The key to effective compression lies in understanding how KVM handles storage:
1. Guest OS layer (NTFS allocation) 2. Virtual disk format (qcow2/raw) 3. Host filesystem (ext4/xfs/zfs) 4. Physical storage (SSD/HDD)
Here's the complete workflow I've developed through extensive testing:
# Inside Windows guest (PowerShell):
Optimize-Volume -DriveLetter C -ReTrim -Defrag -SlabConsolidate
sdelete64.exe -z c:
# On KVM host:
virsh shutdown win7-vm
qemu-img convert -O qcow2 -c \
-o cluster_size=512K,preallocation=metadata \
win7-original.qcow2 win7-compressed.qcow2
virt-sparsify --compress --tmp ./tempdir \
win7-compressed.qcow2 win7-final.qcow2
For truly professional results, consider these additional steps:
# 1. Remove Windows features (PowerShell):
Get-WindowsOptionalFeature -Online |
Where-Object {$_.State -eq "Enabled"} |
Disable-WindowsOptionalFeature -Online -NoRestart
# 2. Clean update backups:
DISM /Online /Cleanup-Image /StartComponentCleanup /ResetBase
# 3. Optimize pagefile:
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" \
/v PagingFiles /t REG_MULTI_SZ /d "C:\pagefile.sys 1024 1024" /f
The snapshot preservation challenge requires a different approach:
# Create a new base image while maintaining snapshot chain:
virsh snapshot-create-as win7-vm temp-snapshot --disk-only
qemu-img convert -O qcow2 win7-original.qcow2 win7-newbase.qcow2
virsh blockpull --domain win7-vm --path /var/lib/libvirt/images/win7-original.qcow2
virsh snapshot-delete win7-vm temp-snapshot --metadata
Test results from my production environment:
Method | Original Size | Compressed Size | Time |
---|---|---|---|
Basic qemu-img | 100GB | 91GB | 25min |
Full optimization | 100GB | 14.7GB | 2h |
Enterprise method | 100GB | 8.2GB | 4h |
The enterprise method includes Sysprep generalization, component removal, and LZMA compression at the host level.
For regular maintenance, I use this bash script:
#!/bin/bash
VM_NAME="win7-prod"
SNAPSHOT_PREFIX="temp-optimize-"
# Create temporary snapshot
virsh snapshot-create-as $VM_NAME $SNAPSHOT_PREFIX$(date +%s) \
--disk-only --atomic --quiesce
# Get image path
IMG_PATH=$(virsh domblklist $VM_NAME | grep vda | awk '{print $2}')
# Compress image
qemu-img convert -O qcow2 -c -o cluster_size=512K "$IMG_PATH" \
"${IMG_PATH}.optimized"
# Swap images
mv "$IMG_PATH" "${IMG_PATH}.old"
mv "${IMG_PATH}.optimized" "$IMG_PATH"
# Commit changes
virsh blockcommit $VM_NAME vda --active --pivot
# Cleanup
rm "${IMG_PATH}.old"
When working with KVM virtual machines running Windows guests, many administrators hit the same frustrating wall: traditional methods like zero-filling and qemu-img convert -c
often yield disappointing results. The gap between theoretical compression potential and actual output can be massive - as seen when a 100GB disk only shrinks to 91GB despite appearing to have 60GB free space.
The secret behind ModernIE's compact Windows 7 VMs (under 10GB) lies in their specialized preparation process:
# Example of ModernIE-style optimization steps
1. sysprep /generalize /oobe /shutdown
2. sdelete -z c:
3. DISM /Online /Cleanup-Image /StartComponentCleanup /ResetBase
4. compact /compactos:always
5. Manual removal of unnecessary components (language packs, etc.)
Here's my tested approach that consistently delivers better results than basic methods:
# Inside Windows guest:
sdelete -z c:
diskpart
select volume c
shrink desired=10240 # Target size in MB
exit
# On KVM host:
virt-sparsify --compress --tmp /path/to/tmp win7.qcow2 win7-compressed.qcow2
qemu-img convert -O qcow2 -c win7-compressed.qcow2 win7-final.qcow2
The snapshot preservation challenge requires a different approach. Here's how to handle it:
# Create a new base image incorporating snapshots
virsh snapshot-create-as --domain vm1 snap1
qemu-img rebase -b new_base.qcow2 vm1.qcow2
qemu-img commit vm1.qcow2
virt-sparsify --in-place vm1.qcow2
For extreme cases, consider these additional steps:
- Convert from qcow2 to raw format before compression
- Use xz compression with custom settings:
qemu-img convert -O qcow2 -o compression_type=zstd,compression_level=19
- Pre-allocate storage to eliminate fragmentation:
fallocate -l 10G new_image.qcow2
Always verify your results with:
qemu-img info vm-image.qcow2
virt-filesystems --long -h --all -a vm-image.qcow2
du -h --apparent-size vm-image.qcow2