Running qemu-img convert -O qcow2 -c alone does not reduce image size when the guest uses LUKS encryption. LUKS encrypts all blocks — including free space — making them appear allocated to the host. TRIM signals must pass through the full stack: guest → LUKS → virtio-disk → qcow2.
- Host: Linux with libvirt (
qemu:///system) - Guest: Linux with LUKS-encrypted root filesystem
- Image format: qcow2
Guest fstrim
↓
LUKS (allow_discards)
↓
virtio-disk (discard=unmap via libvirt XML)
↓
qcow2 (cluster deallocation)
↓
qemu-img convert (skips unallocated clusters)
Edit /etc/crypttab and add the discard option to your LUKS entry:
# /etc/crypttab
# <name> <device> <keyfile> <options>
luks-<uuid> UUID=<your-uuid> none luks,discard
Edit /etc/fstab and add discard to the mount options:
# /etc/fstab
/dev/mapper/luks-<uuid> / ext4 defaults,discard 0 1
Rebuild initramfs and reboot:
sudo update-initramfs -u -k all
sudo rebootVerify discard is active after reboot:
sudo dmsetup table | grep allow_discards
# Must show 'allow_discards' in outputWhy: Without this, TRIM signals from the guest are dropped before reaching the qcow2 file.
Find the correct libvirt URI (system vs session):
virsh -c qemu:///system list --allEdit the VM XML:
virsh -c qemu:///system edit <vm-name>Find the <driver> line inside the <disk> block and add discard="unmap":
<driver name="qemu" type="qcow2" discard="unmap"/>Security note: Enabling discard on LUKS leaks metadata about which blocks are unused. File content remains encrypted. Acceptable tradeoff for local VMs; avoid on shared or untrusted storage.
Start the VM and run:
sudo fstrim -av
# Output must report actual bytes discardedShutdown cleanly:
sudo poweroffqemu-img convert -O qcow2 -c -p mxlinux.qcow2 mxlinux_new.qcow2-O qcow2— output format-c— apply compression-p— show progress
du -sh mxlinux_new.qcow2
qemu-img info mxlinux_new.qcow2 | grep -E "disk size|virtual size"
# disk size must reflect actual guest usage
# virtual size stays unchanged (expected)Add to your shell config (~/.bashrc or ~/.zshrc):
export LIBVIRT_DEFAULT_URI="qemu:///system"