Compacting Client Hyper-V VHDX Files

Virtual hard disks have the same tendency to grow in size as regular disks have to fill up. Deduplication is a great way to battle this, but unfortunately it is not available for Windows 8 Client Hyper-V. I know that hacks are available describing how to transfer the relevant DLLs from Server 2012 but I value my data too much to try that. The only thing left in order to regain valuable (SSD) disk space is to compact the VHDX. That, however, is more difficult than it should be.

This article shows how to compact Windows VMs. For a description of how to compact Ubuntu Linux VMs take a look at this post.

Start Inside

Before bringing out the heavy guns do the obvious:

  • Make sure KB2852386 is installed
  • Run Disk Cleanup with admin permissions

These simple things unearth treasures like the following:

Windows 7 Disk Cleanup

Do a full reboot after running Disk Cleanup because part of the removal happens when the system next starts up.

Zero, Defragment and Shrink

Zero

Run Sysinternals’ Sdelete tool to zero all the deleted bits on disk:

sdelete -s -z c:

Defragment

While defragmenting is not strictly necessary it increases the chances that the shrinking process in the next step is successful.

Shrink

Go to Disk Management and shrink your partition(s) as much as possible. This is the most important part. Without this Hyper-V unfortunately is not able to compact the VHDX significantly.

Compact VHDX from the GUI

When you go to a virtual machine’s settings and edit the hard drive, you are presented with a dialog box offering to compact the disk:

Hyper-V - Edit Virtual Hard Disk - Compact

That is exactly what you want. You select it, click next and … the wizard finishes after a few (too few) seconds and the VHDX size remains unchanged.

Compact VHDX from PowerShell

I am not a big fan of dumbing down or even breaking the UI by making relevant functionality only available through PowerShell as seems to be the trend these days. This seems to be such a case. For some strange reason the Compact VHDX UI does nothing and you have to resort to PowerShell to do the job. Sad.

The relevant command is Optimize-VHD. In order for the optimization to succeed the virtual hard disk needs to be mounted first with Mount-VHD (both commands work on VHD and VHDX files).

Automation

You probably have more than one virtual machine on your PC or laptop. In order to shrink them all manually shut them down first. Then run the following command, replacing D:\VMs with the base path for your VHDX files:

gci -File -Filter *.vhd* -Path D:\VMs -Recurse | % {Mount-VHD $_.FullName -ReadOnly; Optimize-VHD $_.FullName -Mode full; Dismount-VHD $_.FullName}

Errors

The PowerShell code may report errors. Possible causes include

  • The VHDX is in use (the VM is probably not turned off)
  • The VHDX is a differencing disk and the parent disk is not properly connected any more

Analysis & Conclusion

The implementation of dynamically expanding virtual disks in Hyper-V leaves room for improvement, at least when looking at disk space requirements. Reclaiming (host) disk space by compacting the VHDX files succeeds only when there is unpartitioned space at the end of the virtual hard disk. That typically involves shrinking a partition from inside the VM.

A more efficient implementation could make use of the trim command to keep track which parts of a disk are not needed any more and use that information to reduce the size of the VHDX upon request.

Lacking such a more advanced algorithm be prepared to throw disk space at the problem of expanding VHDX files that won’t compact (much). Host-based deduplication would be the answer – if only it were available on client versions of Windows.

,

15 Responses to Compacting Client Hyper-V VHDX Files

  1. Brian October 3, 2014 at 16:31 #

    Hi Helge

    tried this but I think sometimes there may be an issue if there’s a large solid file high up in the Hyper-V VM partition and the defragmenter (at least the one in Windows) leaves it there. Do you know if there is another defrag tool that will free up space so we can successfully shrink the partition?
    Thanks!

  2. Fabrizio February 9, 2015 at 16:55 #

    Hi, thanks for the article.

    I’m confused because you stated that you had not used the cmdlet Optimize-VHD for a very simple reason: that did not reduce the size of the VHDX.

    However the article *IS* based on Optimize-VHD so I really do not understand your last bullet.

    Could you please clarify?

    Thanks

    Kind Regards

  3. Stephen April 18, 2015 at 08:40 #

    Hi Helge,

    Great article. Just stumbled across it and ran it on a Windows 7 vhdx that was 31GB on Hyper-V but “only” 15GB according to the disk. After about a minute, the size was reduced to 17GB on Hyper-V.

    Great stuff as always.

    Thanks,

    Stephen

  4. Dan Gough June 30, 2015 at 10:00 #

    Hi, after running your example script I was unable to start any of the VMs. Solution here: http://toddbaginski.com/blog/fixing-a-broken-hyper-v-vhd-chain

    Mounting the VHD changes some identifiers and breaks the chain when you have snapshots. But the script seems to work just as well if you remove the mount/dismount commands!

  5. Khurram August 24, 2015 at 18:46 #

    Mounting the VHD as read only first will allow you to run compact from the GUI. Otherwise, as you stated, nothing will happen

  6. Dom September 8, 2015 at 00:55 #

    A few corrections to this article:

    It is not necessary to zero deleted bits. Both Optimize-VHD and the Hyper-V GUI Compact Option will release unused space however you must have the VHDX mounted as Read Only otherwise both tools will do nothing and report no error.

    I found Optimize-VHD in Full Mode ran for 20+ minutes and reduced a 911GB VHD down to 600GB. I then ran the Compact option in Hyper-V and it took the 600GB file and reduced it to 250GB but took much much longer.

    So I’d suggest new users:
    1) Shut down any Virtual Machines using the VHDx
    2) Use Disk Management to attach the VHDx as Read Only
    3) Open the Hyper-V MMC -> Edit Disk -> Compact and select the VHDx and wait.
    4) Unmount the VHDx using Disk Management.

  7. James November 17, 2015 at 14:19 #

    Regarding Dom’s comment, I found that plan didn’t work. The sdelete option takes a long time. A fast effective solution using diskpart can be found here: https://fiddley.wordpress.com/2014/01/27/dynamically-expanding-vhd-not-compacting-in-hyper-v/

  8. Robert February 12, 2016 at 22:49 #

    Powershell to optimize all the VHDs on VMs that are Off.
    Get-vm | where {$_.state -eq “Off”} | Get-VMHardDiskDrive | Select Path |Optimize-VHD

  9. Alaska Computer Guy March 27, 2016 at 04:13 #

    I was frustrated when compacting virtual hard disks didn’t do much from within Hyper V and I resolved to find a better way without using third party tools. It took me a bit of research to learn how to do this but after reviewing lots of options, I settled on the method below. I like the fact that you don’t need to shrink partitions in your vdisk and you don’t even need to zero out unused disk space.

    From an elevated command prompt type:

    diskpart
    select vdisk file=”C:\Hyper-V\sampledrive.vhdx”
    attach vdisk readonly
    compact vdisk
    detach vdisk
    exit

    This process reduced the size of my Master Image from 24GB to 15GB and takes seconds. It may sound too good to be true but I would be very happy to hear back from you if it worked for you.

    • superwowie August 5, 2017 at 01:58 #

      Thanks so much. had no idea the compact command was in diskpart. as on my hyper machine client hyper v tools are not provided, neither powertools hyper commands

  10. Jeff Rozar May 29, 2016 at 13:18 #

    I had an Exchange server disk that was using 1.3TB due to log files taking up 1.1TB! I removed those files, making the C: drive of the Exchange server only 200GB, so I wanted to shrink this space down because backups were taking way too long!

    Treied all of the above, but none of the above worked for me, so I did this:

    Power off the VM
    In Computer Management/Disk Management, right-click and choose Attach VHD
    Right-click on the partition and choose Shrink Volume
    Depending on the use of the VM, I added some headroom (I removed 750GB instead of the 1 TB it was recommending).
    After the shrinking was complete (there was no UI), I had 750GB in Unallocated space at the end of the disk.
    I right-clicked on the Disk (not any partition on the disk) and chose Detach VHD.

    Then I did these steps:

    diskpart
    select vdisk file=”D:\HV\EXCH\EXCH.vhdx”
    attach vdisk readonly
    compact vdisk
    detach vdisk
    exit

    Shrunk!

    • Paul April 26, 2017 at 09:50 #

      Thank you. Worked a treat

  11. Brandon June 18, 2016 at 01:37 #

    THANK YOU! This worked like a charm on Windows VM’s, but with generation 2 Linux VM’s using XFS running on Hyper-V 2012 R2 I had to follow the guide at http://www.1337admin.org/uncategorized/compacting-virtual-disks-on-generation-2-linux-hyper-v-2012-r2-vms/ I hope this saves someone else a few days of work as well.

  12. user135711 September 28, 2017 at 10:49 #

    Using the hypervisor administrator in 2016 server, the time expensive sdelete step, or zeroing free space is no longer necessary. I just ran disk compact after deleting 150gb without zeroing anything and the size on disk was compacted to the 38GB’s stored on disk and the virtual disk size unchanged. This useless step is all over the web for some reason.

Leave a Reply