Testing system updates using libvirts checkpoint feature

If you want to test upgrades on virtual machines (running on libvit/qemu/kvm) these are usually the most common steps:

  • Clone the virtual machine and test the upgrade.
  • Create a snapshot beforehand, do an in-place upgrade and hope everything works. If not, revert the snapshot..
  • Combine both methods
  • Use LVM or filesystem snapshots (snapper, etc)

As with recent versions, both libvirt and qemu have full support for dirty bitmaps (so called checkpoints). These checkpoints, once existent, will track changes to the block level layer and can be exported via NBD protocol.

Usually one can create these checkpoints using virsh checkpoint-create[-as], with a proper xml description.

Using the pull based model, the following is possible:

  • Issue an backup-begin statement and freeze the virtual machines file systems during this process (using qemu-agent) so you get an consistent system state.
  • Create an qcow2 image with backing image option that points to the created (readonly) NBD export via unix socket as overlay.
  • Use the created overlay image to boot up the instance and test the system upgrade.
  • Do so while the virtual machine is operating without any downtime.

The overlay image will only use the disk space for the blocks changed during upgrade: no need to create a full clone which may waste a lot of disk space.

In order to simplify the first step, its possible to use virtnbdbackup for creating the required consistent checkpoint and export its data using a unix domain socket.

Update: As alternative, ive just created a small utility called vircpt to create and export checkpoints.

In my example im using a debian11 virtual machine with qemu guest agent configured:

# virsh list --all
 Id Name State 
 1 debian11_default running

Now let virtnbdbackup create an checkpoint, freeze the filesystems during creation and tell libvirt to provide us with a usable NBD server listening on an unix socket:

# virtnbdbackup -d debian11_default -o /tmp/foo -s
INFO lib common - printVersion [MainThread]: Version: 1.9.45 Arguments: ./virtnbdbackup -d debian11_default -o /tmp/foo -s
INFO root virtnbdbackup - main [MainThread]: Local NBD Endpoint socket: [/var/tmp/virtnbdbackup.5727] 
INFO root virtnbdbackup - startBackupJob [MainThread]: Starting backup job.
INFO fs fs - freeze [MainThread]: Freezed [2] filesystems. 
INFO fs fs - thaw [MainThread]: Thawed [2] filesystems. 
INFO root virtnbdbackup - main [MainThread]: Started backup job for debugging, exiting.

We can now use nbdinfo to display some information about the NBD export:

# nbdinfo "nbd+unix:///vda?socket=/var/tmp/virtnbdbackup.5727" 
    protocol: newstyle-fixed without TLS, using structured packets 
    export-size: 137438953472 (128G) 
        DOS/MBR boot sector uri: nbd+unix:///vda?socket=/var/tmp/virtnbdbackup.5727

And create a backing image that we can use to test an in-place upgrade:

# qemu-img create -F raw -b nbd+unix:///vda?socket=/var/tmp/virtnbdbackup.5727 -f qcow2 upgrade.qcow2

Now we have various ways for booting the image:

  • Create another domain config in libvirt which points to this disk image, boot.
  • Extract the current qemu command line for the the running domain using virsh domxml-to-native qemu-argv --domain debian11_default and execute it manually after adjusting required parameters.
  • Trust for everything to work out in the simplest way and simply boot up the image via:
# qemu-system-x86_64 -hda upgrade.qcow2 -m 2500 --enable-kvm


After performing the required tests within the virtual machine we can simply kill the active NBD “backup job”:

# virtnbdbackup -d debian11_default -o /tmp/foo -k
INFO lib common - printVersion [MainThread]: Version: 1.9.45 Arguments: ./virtnbdbackup -d debian11_default -o /tmp/foo -k 
INFO root virtnbdbackup - main [MainThread]: Stopping backup job

And remove the created qcow image:

# rm -f upgrade.qcow2
Written on October 15, 2023