Skip to content

A VHDX never shrinks by itself, and checkpoints multiply it

Dynamic disks grow and stay grown, checkpoints add a file each, and the compaction step has a prerequisite people skip.

6 min read

A dynamically expanding VHDX grows as the guest writes and does not shrink when the guest deletes. Delete 40 GB inside the virtual machine and the file on your drive stays exactly the size it reached. Nothing about this is a fault, and it surprises everybody once.

What each file is

ExtensionWhat it is
.vhdxThe virtual disk itself
.avhdxA differencing disk created by a checkpoint, one per checkpoint
.vmrs and .binSaved machine state, roughly the guest's memory size
.vmcxThe configuration, tiny
# Every virtual disk, its file size and what the guest thinks it has
Get-VM | Get-VMHardDiskDrive | ForEach-Object {
  $v = Get-VHD -Path $_.Path
  [PSCustomObject]@{
    GB       = [math]::Round($v.FileSize/1GB,1)
    MaxGB    = [math]::Round($v.Size/1GB,1)
    Type     = $v.VhdType
    Parent   = [bool]$v.ParentPath
    Path     = $_.Path
  }
} | Sort-Object GB -Descending

Parent being true means this is a differencing disk sitting on top of another, which is what a checkpoint creates. A chain of five checkpoints is five files, each holding the changes since the one below it.

Compacting, and the step that is skipped

  1. Inside the guest, delete what you meant to delete and then zero the free space. Without this, compaction finds nothing to remove because the blocks still contain old data.
  2. Shut the machine down. Compaction needs the disk offline, not saved or paused.
  3. Optimize-VHD with mode Full, from an elevated prompt.
  4. Check the result, since a disk that was always full will not shrink and that is the correct outcome.
# In the guest, then shut it down
# (sdelete -z on Windows guests, fstrim or dd+rm on Linux guests)

# On the host, elevated
Optimize-VHD -Path 'D:\VMs\dev.vhdx' -Mode Full

# Before and after
(Get-VHD 'D:\VMs\dev.vhdx').FileSize / 1GB

Checkpoints are the bigger number

Each checkpoint freezes the disk and sends new writes to a new file. Keep a machine running for months on a checkpoint taken before an upgrade, and the differencing disk can be larger than the original. Merging them back is the fix, and it is a delete in the user interface.

# What exists, oldest first
Get-VMSnapshot -VMName 'dev' | Sort-Object CreationTime |
  Select-Object Name, CreationTime, ParentSnapshotName

# Merge one back into the parent. The data is kept, the file is not.
Remove-VMSnapshot -VMName 'dev' -Name 'before upgrade'

The same problem, in three other places

Windows Sandbox creates and discards a disk per session, so it costs space while running and nothing afterwards. WSL distributions use the same VHDX format and the same never shrinks rule. Docker Desktop keeps its images in one, which is why its disk grows far beyond the size of the images you can list.

On a machine where all of these are present, moving them to a second drive once is worth more than compacting any of them repeatedly, which is the argument in storage for a Windows dev machine.

Common questions

Why does my VHDX not shrink when I delete files?

Because a dynamically expanding disk grows as the guest writes and never releases blocks on its own. Zero the free space inside the guest, shut it down, then run Optimize-VHD -Mode Full.

What are .avhdx files?

Differencing disks created by checkpoints. Each one holds the changes made since the checkpoint below it, and a long lived checkpoint can grow larger than the original disk.

Does deleting a Hyper-V checkpoint lose my work?

No. Removing a checkpoint merges its changes into the parent disk. What you lose is the ability to return to that point in time.

How much space does Windows Sandbox use?

It creates a disk per session and discards it when the window closes, so the cost is temporary. The base image it builds from is part of Windows itself.

Read next