🡠 Back to all articles

How VHDs are created/merged when creating/removing VM checkpoints in Hyper-V

Contents

  1. TLDR
  2. Creating a checkpoint
  3. Merging VHDs when removing a checkpoint from a linear checkpoints sequence
  4. Removing a checkpoint with multiple child checkpoints

TLDR

Removing a checkpoint that has one child checkpoint:

Removing a checkpoint that has multiple child checkpoints:

And this is what happens when we get rid of branching in the checkpoints tree:

Continue reading if you want to see how to check it by yourself in Hyper-V.

Creating a checkpoint

First let’s see what happens with VHDs when checkpoints are created. We have a virtual machine named SRV01 that has no checkpoints:

This VM has one VHD:

PS C:\> (Get-VM SRV01).HardDrives.Path E:\VMs\SRV01\Virtual Hard Disks\SRV01.vhdx

Let’s visualize it:

Now let’s create a checkpoint:

This is what happened with the VHD of the VM when when the checkpoint was created:

Let’s visualize it again:

Now we know what happens with VHDs when checkpoints are created. In the following sections we will see what happens with VHDs when checkpoints are removed. There are two situations that may occur when a checkpoint is removed:

  1. Checkpoint being removed has only one child checkpoint.
  2. Checkpoint being removed has multiple child checkpoints.

We will take a look at both.

Removing a checkpoint with one child checkpoint

Let’s take a VM with checkpoints created sequentially one after the other, so that each checkpoint has one child checkpoint:

Each checkpoint has its VHD:

PS C:\> (Get-VM SRV01).HardDrives.Path.Split("\")[-1] SRV01_BAA8A125-E1F5-4203-AE2F-B99A5AEEDF44.avhdx
PS C:\> Get-VMCheckpoint SRV01 | FT Name, @{ N = "Disk"; E = { $_.HardDrives.Path.Split("\")[-1] } } Name Disk ---- ---- Checkpoint1 SRV01.vhdx Checkpoint2 SRV01_4960DD5A-FA60-49D3-9F75-D85ABF8430DB.avhdx Checkpoint3 SRV01_BC624811-1E39-4CF6-921B-BE5755B95B0D.avhdx

So, this is what we have:

Now let’s remove Checkpoint2 from this sequence:

PS C:\> Get-VMCheckpoint -VMName SRV01 -Name Checkpoint2 | Remove-VMCheckpoint

And see how the VHD chain was changed:

PS C:\> (Get-VM SRV01).HardDrives.Path.Split("\")[-1] SRV01_BAA8A125-E1F5-4203-AE2F-B99A5AEEDF44.avhdx
PS C:\> Get-VMCheckpoint SRV01 | FT Name, @{ N = "Disk"; E = { $_.HardDrives.Path.Split("\")[-1] } } Name Disk ---- ---- Checkpoint1 SRV01.vhdx Checkpoint3 SRV01_4960DD5A-FA60-49D3-9F75-D85ABF8430DB.avhdx
PS C:\> Get-VHD "E:\VMs\SRV01\Virtual Hard Disks\SRV01_BAA8A125-E1F5-4203-AE2F-B99A5AEEDF44.avhdx" ` >> | FL VhdType, Path, ParentPath VhdType : Differencing Path : e:\vms\srv01\virtual hard disks\srv01_baa8a125-e1f5-4203-ae2f-b99a5aeedf44.avhdx ParentPath : E:\VMs\SRV01\Virtual Hard Disks\SRV01_4960DD5A-FA60-49D3-9F75-D85ABF8430DB.avhdx

So, the following happened:

Let’s show these changes on our diagram:

Removing a checkpoint with multiple child checkpoints

Now let’s see what happens when the checkpoints tree has branching:

PS C:\> (Get-VM SRV01).HardDrives.Path.Split("\")[-1] SRV01_66421382-6DBA-4776-AD1D-FEF2B54EEE0A.avhdx
PS C:\> Get-VMCheckpoint SRV01 ` >> | FT Name, ParentCheckpointName, @{ N = "Disk"; E = { $_.HardDrives.Path.Split("\")[-1] } } Name ParentCheckpointName Disk ---- -------------------- ---- Checkpoint1 SRV01.vhdx Checkpoint2 Checkpoint1 SRV01_10A6AB3D-7C3C-4B56-9ABF-0C1EC1556E79.avhdx Checkpoint3A Checkpoint2 SRV01_11D906EA-CFB1-461E-A07F-2243174DE123.avhdx Checkpoint3B Checkpoint2 SRV01_A572E935-C4F1-491A-AA16-284A2EBE9DE5.avhdx
PS C:\> Get-VHD "E:\VMs\SRV01\Virtual Hard Disks\SRV01_11D906EA-CFB1-461E-A07F-2243174DE123.avhdx" ` >> | FL VhdType, Path, ParentPath VhdType : Differencing Path : e:\vms\srv01\virtual hard disks\srv01_11d906ea-cfb1-461e-a07f-2243174de123.avhdx ParentPath : E:\VMs\SRV01\Virtual Hard Disks\SRV01_10A6AB3D-7C3C-4B56-9ABF-0C1EC1556E79.avhdx
PS C:\> Get-VHD "E:\VMs\SRV01\Virtual Hard Disks\SRV01_A572E935-C4F1-491A-AA16-284A2EBE9DE5.avhdx" ` >> | FL VhdType, Path, ParentPath VhdType : Differencing Path : e:\vms\srv01\virtual hard disks\srv01_a572e935-c4f1-491a-aa16-284a2ebe9de5.avhdx ParentPath : E:\VMs\SRV01\Virtual Hard Disks\SRV01_10A6AB3D-7C3C-4B56-9ABF-0C1EC1556E79.avhdx

Let’s remove Checkpoint2. This checkpoint has two child checkpoints, and VHDs of that child checkpoints can’t be merged into SRV01...79.avhdx.

PS C:\> Get-VMCheckpoint -VMName SRV01 -Name Checkpoint2 | Remove-VMCheckpoint

Again, let’s see the changes of the VHD chains:

Get-VMCheckpoint SRV01 ` >> | FT Name, ParentCheckpointName, @{ N = "Disk"; E = { $_.HardDrives.Path.Split("\")[-1] } } Name ParentCheckpointName Disk ---- -------------------- ---- Checkpoint1 SRV01.vhdx Checkpoint3A Checkpoint1 SRV01_11D906EA-CFB1-461E-A07F-2243174DE123.avhdx Checkpoint3B Checkpoint1 SRV01_A572E935-C4F1-491A-AA16-284A2EBE9DE5.avhdx
Get-VHD "E:\VMs\SRV01\Virtual Hard Disks\SRV01_11D906EA-CFB1-461E-A07F-2243174DE123.avhdx" ` >> | FL VhdType, Path, ParentPath VhdType : Differencing Path : e:\vms\srv01\virtual hard disks\srv01_11d906ea-cfb1-461e-a07f-2243174de123.avhdx ParentPath : E:\VMs\SRV01\Virtual Hard Disks\SRV01_10A6AB3D-7C3C-4B56-9ABF-0C1EC1556E79.avhdx
Get-VHD "E:\VMs\SRV01\Virtual Hard Disks\SRV01_A572E935-C4F1-491A-AA16-284A2EBE9DE5.avhdx" ` >> | FL VhdType, Path, ParentPath VhdType : Differencing Path : e:\vms\srv01\virtual hard disks\srv01_a572e935-c4f1-491a-aa16-284a2ebe9de5.avhdx ParentPath : E:\VMs\SRV01\Virtual Hard Disks\SRV01_10A6AB3D-7C3C-4B56-9ABF-0C1EC1556E79.avhdx
PS C:\> Get-ChildItem "E:\VMs\SRV01\Virtual Hard Disks" | FT Name Name ---- SRV01.vhdx SRV01_10A6AB3D-7C3C-4B56-9ABF-0C1EC1556E79.avhdx SRV01_11D906EA-CFB1-461E-A07F-2243174DE123.avhdx SRV01_66421382-6DBA-4776-AD1D-FEF2B54EEE0A.avhdx SRV01_A572E935-C4F1-491A-AA16-284A2EBE9DE5.avhdx

We see that:

And the diagram is:

Finally, let’s see one more example: what will happen if we remove branching by removing Checkpoint3B. By doing so we will remove SRV01...E5.avhdx, and nothing should prevent SRV01...23.avhdx from merging into SRV01...79.avhdx:

PS C:\> Get-VMCheckpoint -VMName SRV01 -Name Checkpoint2 | Remove-VMCheckpoint

The updated diagram:

We see that after SRV01...E5.avhdx had been removed, SRV01...23.avhdx was merged into SRV01...79.avhdx.