Update: This got significantly simpler with newer qemu and virt-manager, read an updated post.
If you use VMs a lot, you know that with the most popular qcow2 disk format, the disk image starts small, but grows with every filesystem change happening inside the VM. Deleting files inside the VM doesn’t shrink it. Of course that wastes a lot of disk space on your host – the VMs often contain gigabytes of freed space inside the VM, but not on the host. Shrinking the VM images is possible, but tedious and slow. Well, recently I learned that’s actually not true anymore. You can use the TRIM command, used to signalize to SSD drives that some space can be freed, to do the same in virtualization stack – signalize from VM to host that some space can be freed, and the disk image shrunk. How to do that? As usual, this is a shameless copy of instructions found elsewhere on the Internets. The instructions assume you’re using virt-manager or libvirt directly.
First, you need to using qcow2 images, not raw images (you can configure this when adding new disks to your VM).
Second, you need to set your disk bus to SCSI (not VirtIO, which is the default).
Third, you need to set your SCSI Controller to VirtIO SCSI (not hypervisor default).
Fourth, you need to edit your VM configuration file using
virsh edit vmname and adjust your hard drive’s driver line to include
discard='unmap', e.g. like this:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' discard='unmap'/>
And that’s it. Now you boot your VM and try to issue:
$ sudo fstrim -av
/boot: 319.8 MiB (335329280 bytes) trimmed
/: 101.5 GiB (108928946176 bytes) trimmed
You should see some output printed, even if it’s just
0 bytes trimmed, and not an error.
If you’re using LVM, you’ll also need to edit
/etc/lvm/lvm.conf and set:
issue_discards = 1
Then it should work, after a reboot.
Now, if you want trimming to occur automatically in your VM, you have two options (I usually do both):
Enable the fstrim timer that trims the system once a week by default:
$ sudo systemctl enable fstrim.timer
And configure the root filesystem (and any other one you’re interested in) to issue discard command automatically after each file is deleted. Edit
/etc/fstab and add a
discard mount option, like this:
UUID=6d368798-f4c2-44f9-8334-6be3c64cc449 / ext4 defaults,discard 1 1
And that’s it. Try to create a big file using dd, watch your VM image grow. Then delete the file, watch the image shrink. Awesome. If only we had this by default.