The QCOW2 format's sparse allocation is fantastic for storage efficiency, but requires manual intervention when you want to reclaim deleted space. Here's what's happening under the hood:
# Initial creation (sparse allocation)
qemu-img create -f qcow2 vm_disk.qcow2 100G
# Actual disk usage after install:
du -sh vm_disk.qcow2
# Shows ~10GB despite 100G virtual size
First, we need to zero out the deleted space from within the guest OS:
# For Linux guests:
sudo dd if=/dev/zero of=/zero.fill bs=1M
sudo rm -f /zero.fill
# For Windows guests (PowerShell):
Write-Zero -Path C:\zero.fill -Size 1GB
Remove-Item C:\zero.fill
After zeroing, use qemu-img to convert and optimize:
qemu-img convert -O qcow2 -c \
vm_disk.qcow2 vm_disk_compressed.qcow2
# Check the results:
qemu-img info vm_disk_compressed.qcow2
ls -lh vm_disk*.qcow2
For live systems, use virt-sparsify (requires libguestfs-tools):
sudo apt-get install libguestfs-tools
virt-sparsify --in-place vm_disk.qcow2
Here's a bash script to handle the complete process:
#!/bin/bash
VM_IMAGE=$1
TEMP_IMAGE="${VM_IMAGE%.*}_temp.qcow2"
echo "1. Zeroing free space in guest..."
# Use guestfish or virt-sysprep as needed
echo "2. Converting image..."
qemu-img convert -O qcow2 -c "$VM_IMAGE" "$TEMP_IMAGE"
echo "3. Replacing original..."
mv "$TEMP_IMAGE" "$VM_IMAGE"
echo "Done. Reclaimed space:"
ls -lh "$VM_IMAGE"
The QCOW2 (QEMU Copy-On-Write version 2) format is a popular choice for KVM/QEMU virtual machines due to its space-efficient design. While it dynamically expands as data is written, it doesn't automatically shrink when files are deleted within the VM. This behavior occurs because:
- The filesystem marks space as available but doesn't zero it out
- QCOW2 maintains its allocation tables for performance reasons
- The format wasn't originally designed for automatic shrinking
Before attempting to shrink a QCOW2 image:
# Check current allocation
qemu-img info vm_image.qcow2
# Verify filesystem free space inside VM
df -h
lsblk
For Linux guests:
# For ext2/3/4 filesystems
sudo dd if=/dev/zero of=/zero.fill bs=1M
sudo rm -f /zero.fill
# Alternative method
sudo fstrim -v /
# For XFS
sudo xfs_fsr -v /
For Windows guests:
sdelete -z C:
Shutdown the VM and run:
# Create a temporary converted image
qemu-img convert -O qcow2 -c vm_image.qcow2 vm_image_compressed.qcow2
# Verify the new size
qemu-img info vm_image_compressed.qcow2
# Replace original (make backup first!)
mv vm_image_compressed.qcow2 vm_image.qcow2
For more sophisticated operations:
# Install libguestfs-tools first
sudo apt-get install libguestfs-tools
# Run sparsify operation
virt-sparsify --in-place vm_image.qcow2
Here's a bash script to automate the process:
#!/bin/bash
VM_IMAGE=$1
# Step 1: Connect to guest and zero free space
# (This requires guest tools or SSH access)
# Step 2: Compact image
echo "Compacting $VM_IMAGE..."
TEMP_IMAGE="${VM_IMAGE%.*}_temp.qcow2"
qemu-img convert -O qcow2 -c "$VM_IMAGE" "$TEMP_IMAGE"
# Verify reduction
ORIG_SIZE=$(qemu-img info "$VM_IMAGE" | grep 'virtual size' | awk '{print $3}')
NEW_SIZE=$(qemu-img info "$TEMP_IMAGE" | grep 'virtual size' | awk '{print $3}')
echo "Original size: $ORIG_SIZE"
echo "New size: $NEW_SIZE"
# Replace original
mv "$TEMP_IMAGE" "$VM_IMAGE"
echo "Compaction complete"
- Always backup before shrinking operations
- Schedule regular compaction during maintenance windows
- Monitor disk usage patterns to optimize allocation
- Consider using LVM thin provisioning for dynamic environments
Common issues and solutions:
# If conversion fails due to corruption
qemu-img check vm_image.qcow2
qemu-img amend -o compat=1.1 vm_image.qcow2
# For "Cannot allocate memory" errors
sysctl vm.overcommit_memory=1