How to Reclaim Unused Space and Shrink QCOW2 Disk Images in KVM/QEMU Virtualization


44 views

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