DEV Community

Cover image for Shrinking a VirtualBox disk
Paul Cochrane 🇪🇺
Paul Cochrane 🇪🇺

Posted on • Originally published at peateasea.de on

Shrinking a VirtualBox disk

Can you shrink VirtualBox disk images? It turns out you can, with the right setup. A recent cleanup operation led me to one way to get this particular set of ducks all in line. Here’s what I found out.

Copious collected cruft

Disk drives will do what they always tend to do: fill up with cruft over time. It’s the same with housework. To paraphrase Joan Rivers: you’ve just autocleaned the apt cache, deleted duplicate files, and purged dangling Docker containers, and six months later you have to start all over again.

While freeing up some space on my hard drive recently, I stumbled across a horrifying reality. A VirtualBox disk, created for what should have been a small Vagrant virtual machine (VM), was using up 20 GB of space!

$ cd ~/VirtualBox VMs/<vm-name>_1754671699254_15289
$ ls -ltrh
-rw------- 1 cochrane cochrane 20G Nov 26 18:41 box.vmdk
Enter fullscreen mode Exit fullscreen mode

OMG. 😳 That’s rather a large chunk of the ~430 GB I’ve got on my host system’s main partition. How did it come to this?

The short answer to that question is: Docker.

The longer answer is that this VM was a machine I’d set up to test deployment of an application I’d been developing for a customer recently.1 I’d Dockerised everything nicely, and each time I’d deployed a new version of the application, the deployment pulled in a new Docker image. The thing is, I wasn’t cleaning up old images, hence many dangling images began to collect on the system without me noticing. Oopsie.

Dynamic disk dilemma

Now, Vagrant has this nice thing where it creates a dynamic filesystem image when setting up a new virtual machine. Dynamic, in this sense, means that the filesystem image grows with the disk space demands of the guest system. This is as opposed to using all the allocated filesystem size from the get-go. I use the basic Vagrant setup, hence the VM provider is VirtualBox, which in turn uses the VMDK disk image format by default.

Anyway, since the filesystem image is dynamic, if you give a guest system a total of 100 GB to play with, the filesystem image on the host is only as large as the space that the guest actually uses. Thus, if the VM uses 20 GB of its available 100 GB, then the disk image on the host will also use 20 GB.

But what happens if you clean up the space inside the VM? Does that reduce the size of the disk image on the host? Unfortunately not. At least, it doesn’t in the default configuration. If one sets up things like auto-discard in fstab and uses a shrinkable disk image format (such as VDI), then yes, it’s possible. But since that’s not the standard setup, you’ll have the exploding disk image issue that I ran up against.

In my case here, a quick docker image prune purged many gigabytes of space inside my VM, but didn’t help the situation outside the VM. What to do?

A quick search online revealed a superuser.com answer with a nice set of instructions on how to compact a VDI disk image. What follows is my more detailed exposition of those instructions.

Copy, clone, convert

The first issue to handle is that I didn’t have the right disk image type to support shrinking or compacting. As mentioned earlier, I was using what VirtualBox via Vagrant gives me by default, the VMDK format. What I needed was the VDI format, which is native to VirtualBox and supports filesystem resizing.

Thus, we need to convert the VMDK disk image to VDI format. Fortunately, I’ve already covered this process in an article about resizing Vagrant VM disks.

Before converting the disk image format, be sure to shut down the VM:

$ vagrant halt <vm-name>
==> <vm-name>: Attempting graceful shutdown of VM...
Enter fullscreen mode Exit fullscreen mode

Now is probably a good time to do a backup of the host system. Off you go! I’ll be here waiting for you. 🙂

Ah, you’re back. Great, now I’ll continue with the story.

Enter the directory containing the VirtualBox VM:

$ cd $HOME/VirtualBox\ VMs/<vm-name>_1754671699254_15289
Enter fullscreen mode Exit fullscreen mode

and, assuming that there is enough space on the host system, make a copy of the VMDK file for safe-keeping:

$ cp box.vmdk box.vmdk.keep
Enter fullscreen mode Exit fullscreen mode

You might also want to copy this file somewhere else (such as a completely different computer) just in case things go horribly wrong. You never know!

Now convert the VMDK image to VDI format by cloning it:2

$ vboxmanage clonehd box.vmdk box.vdi --format VDI
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone medium created in format 'VDI'. UUID: 9e7e5ea1-56d2-469e-8f64-44578964f6f4
Enter fullscreen mode Exit fullscreen mode

You’ll find that the VDI image (like the VMDK image it’s based upon) is also ~20 GB in size.

$ ls -ltrh
-rw------- 1 cochrane cochrane 20G Nov 27 16:41 box.vdi
Enter fullscreen mode Exit fullscreen mode

That makes sense; it’s essentially a 1-to-1 copy of the original. It was fortunate that I’d freed up enough space on my host system’s drive before adding all these large temporary files!

Swap similar storage

Ok, now that we’ve got our VDI image, we can attach this new storage medium to the VM using the configuration options mentioned in the “How to compact a VDI file” superuser.com answer. These options will let us trim the filesystem, which will then reduce the VDI disk image size.

But I’m getting a bit ahead of myself. To attach this new storage medium, we need to remove the old one first. To detach a storage medium from a VirtualBox-provided system, one uses the storageattach command:3

$ vboxmanage storageattach <vm-name>_1754671699254_15289 --storagectl 'SATA Controller' \
    --port 0 --medium none
Enter fullscreen mode Exit fullscreen mode

The VMDK image is thus no longer associated with our VM. We then attach the VDI disk image like so:

$ vboxmanage storageattach <vm-name>_1754671699254_15289 \
    --storagectl 'SATA Controller' --port 0 --type hdd --nonrotational=on --discard=on \
    --medium $HOME/VirtualBox\ VMs/<vm-name>_1754671699254_15289/box.vdi
Enter fullscreen mode Exit fullscreen mode

where the important extra options are --nonrotational=on and --discard=on.

The --nonrotational option tells the VM that the disk is an SSD, which then changes the IO behaviour to be better for SSDs. The VirtualBox manual explains this well:

--nonrotational=on | off

Enables you to specify that the virtual hard disk is non-rotational. Some guest OSes, such as Windows 7 or later, treat such disks as solid state drives (SSDs) and do not perform disk fragmentation on them.

The --discard option enables TRIM support on the disk. Again, the VirtualBox documentation is illuminating:

--discard=on | off

Specifies whether to enable the auto-discard feature for a virtual hard disk. When set to on, a VDI image is shrunk in response to a trim command from the guest OS.

The virtual hard disk must meet the following requirements:

  • The disk format must be VDI.
  • The size of the cleared area of the disk must be at least 1 MB.
  • Ensure that the space being trimmed is at least a 1 MB contiguous block at a 1 MB boundary.

You can use other methods to issue trim commands. The Linux fstrim command is part of the util-linux package. Earlier solutions required you to zero out unused areas by using the zerofree or a similar command, and then to compact the disk. You can only perform these steps when the VM is offline.

I’d checked for this support before converting the disk image and found that the output of

$ hdparm -I /dev/sda | grep TRIM
Enter fullscreen mode Exit fullscreen mode

from within the VM gave no output. In other words, before adding the --discard=on option, TRIM support wasn’t available. Since the guest filesystem is ext4, this should be possible, hence why we’re adding it here via the --discard option.

For information about the other options in the storageattach command above, check out my earlier post about resizing Vagrant VM disks.

Remove redundant residue

Now that we’ve attached the cloned VDI disk image to the VM, we can start it again:

$ vagrant up <vm-name>
Bringing machine '<vm-name>' up with 'virtualbox' provider...
Enter fullscreen mode Exit fullscreen mode

and enter it:

$ vagrant ssh <vm-name>
Enter fullscreen mode Exit fullscreen mode

Now we can check again to see if the drive supports TRIM:

vagrant@<vm-name>:~$ sudo hdparm -I /dev/sda | grep TRIM
           * Data Set Management TRIM supported (limit unknown)
Enter fullscreen mode Exit fullscreen mode

Looking good!

In contrast to the description in the How to compact a VDI file superuser.com answer, we’re not setting up auto-discard on the disk here, hence we don’t need to update /etc/fstab.

But we can use fstrim directly as the superuser.com answer describes:

vagrant@<vm-name>:~$ sudo fstrim /
Enter fullscreen mode Exit fullscreen mode

The disk usage within the guest system (Vagrant VM) won’t change, hence df -h won’t show anything different.

Before:

vagrant@<vm-name>:~$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 207M 0 207M 0% /dev
tmpfs 46M 476K 46M 2% /run
/dev/sda1 98G 4.4G 89G 5% /
tmpfs 229M 0 229M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
vagrant 437G 380G 58G 87% /vagrant
tmpfs 46M 0 46M 0% /run/user/1000
Enter fullscreen mode Exit fullscreen mode

After:

vagrant@<vm-name>:~$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 207M 0 207M 0% /dev
tmpfs 46M 476K 46M 2% /run
/dev/sda1 98G 4.4G 89G 5% /
tmpfs 229M 0 229M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
vagrant 437G 380G 58G 87% /vagrant
tmpfs 46M 0 46M 0% /run/user/1000
Enter fullscreen mode Exit fullscreen mode

(Even though the output is identical, yes, I really did check this.)

Although the guest system won’t show any changes, the VDI image on the host system should now be much smaller.

$ ls -ltrh
-rw------- 1 cochrane cochrane 5.8G Nov 27 17:34 box.vdi
Enter fullscreen mode Exit fullscreen mode

This reclaims 15 GB of space on the host system. Awesome!

Purge persisting PC parts

Since we’ve got a running system with a shrinkable filesystem, it’s now safe to remove the old VMDK image. The best way to do this is with VBoxManage’s closemedium command:

$ vboxmanage closemedium disk \
    $HOME/VirtualBox\ VMs/<vm-name>_1754671699254_15289/box.vmdk --delete
Enter fullscreen mode Exit fullscreen mode

You may be wondering why I use a VBoxManage command rather than simply deleting the file with rm. It turns out that attaching a storage medium to a VirtualBox VM also registers that storage medium with it. If we only delete the disk image file, it remains registered with the VM even though the file no longer exists. Such a dangling registration will cause errors if we want to create a VMDK image with the same filename for this VM in the future. I found out about this subtlety while testing the clonehd process I described above.

Wrapping up

I definitely learned something new here: it wasn’t clear to me that one can trim a filesystem of unused blocks. It’s cool that this also reduces a guest system’s disk usage. It was also new to me that TRIM functionality is something beneficial for solid-state media.

Being able to use this functionality to reduce the size of a virtual machine’s disk was a bit of a lifesaver. It allowed me to free up space on my host system while retaining all the data I had in the guest system. It would have been painful to have to recreate everything in the guest system just to cut down on the host system disk usage.

Also, good, detailed documentation rocks. Thank you to whoever put the time and effort into creating the VBoxManage docs!

Addendum: clean up thoroughly before cloning again

Not everything always goes to plan. And so it was while writing this up. Because I wanted to test converting the VMDK image to VDI, I ran the clonehd command that I used above more than once.

In one test run, I removed the .vdi file with rm and ran clonehd a second time. That gave me this error:

$ rm box.vdi
$ vboxmanage clonehd box.vmdk box.vdi --format VDI
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...NS_ERROR_INVALID_ARG
VBoxManage: error: Failed to clone medium
VBoxManage: error: Cannot register the hard disk '$HOME/VirtualBox VMs/<vm-name>_1754671699254_15289/box.vdi' {5e0c4418-9c06-4b88-ab08-298ba834356e} because a hard disk '$HOME/VirtualBox VMs/<vm-name>_1754671699254_15289/box.vdi' with UUID {9e7e5ea1-56d2-469e-8f64-44578964f6f4} already exists
VBoxManage: error: Details: code NS_ERROR_INVALID_ARG (0x80070057), component VirtualBoxWrap, interface IVirtualBox
VBoxManage: error: Context: "RTEXITCODE handleCloneMedium(HandlerArg*)" at line 1208 of file VBoxManageDisk.cpp
Enter fullscreen mode Exit fullscreen mode

Whoops! That doesn’t look good.

What’s going on here? Well, it turns out that VirtualBox registers a storage medium with the VM when cloning. I would guess that the original VMDK image points to its VM, and this pointer is recreated when making the cloned VDI image. Thus, cloning automatically registers the VDI image with the VM.

So, to really remove the VDI file before recreating it, we have to use the closemedium command to remove its entry from the registry:

$ vboxmanage closemedium disk 9e7e5ea1-56d2-469e-8f64-44578964f6f4 --delete
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Enter fullscreen mode Exit fullscreen mode

where in this case I used the disk’s UUID instead of its full path.

Now it’s possible to run the format conversion again:

$ vboxmanage clonehd box.vmdk box.vdi --format VDI
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone medium created in format 'VDI'. UUID: 52d0c913-d7bc-490e-a056-96ac5cdca6aa
Enter fullscreen mode Exit fullscreen mode

Yay! Something else learned!

  1. If you need a persistent, thorough, and experienced software developer with Linux and DevOps experience, give me a yell! I’m available for freelance Python/Perl backend development and maintenance work. Contact me at paul@peateasea.de and let’s discuss how I can help solve your business’s hairiest problems.

  2. See the resizing Vagrant VM disks article for more details and background information.

  3. Think of it as attaching “none” to the VM rather than detaching the disk from the VM.

Top comments (0)