If you want to run virtual machines on Linux, chances are you’re going to use libvirt. I make use of it all the time, especially for testing these blog posts in a clean environment. libvirt provides a common interface around various underlying tools for virtual machine management. It not only offers features for guest management but for networking and storage management as well. It’s standard XML schema also makes for a powerful and versatile configuration format. On Linux, libvirt is typically utilizing KVM, the virtualization layer in the kernel, and, in userspace, QEMU, a generic machine emulator and virtualizer.

Tutorial

This tutorial provides the necessary steps to verify your system supports hardware virtualization and install libvirt on elementary OS 5.1. Most of these steps are the same for Ubuntu 18.04. This tutorial takes into account special considerations for systems using the Btrfs filesystem. There is also a brief section on installing the graphical user interface for libvirt, virt-manager. It is assumed that you are familiar with installing software on Ubuntu, using the command-line, and the Btrfs filesystem.

Check

  1. Check that the system supports hardware virtualization.

    egrep -c '(vmx|svm)' /proc/cpuinfo
    8

    If the output is not zero, then your CPU supports virtualization.

  2. Install the tool for checking that your CPU is compatible with KVM.

    sudo apt -y install cpu-checker
  3. Verify that the system supports KVM.

    kvm-ok
    INFO: /dev/kvm exists
    KVM acceleration can be used

If all checks passed, then you should be able to continue installation of libvirt without issue. Otherwise, you’d better switch to some compatible hardware before proceeding.

Install

  1. If you want to get a more up-to-date virtualization stack, add the virtualization PPA to your system.

    1. The software-properties-common package includes a command for easily adding PPA’s.

      sudo apt -y software-properties-common
    2. Add the virtualization PPA.

      sudo add-apt-repository -uy ppa:jacob/virtualisation
  2. Install libvirt.

    sudo apt -y install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils
  3. Add the current user to the kvm and libvirt groups.

    sudo usermod -aG kvm,libvirt $USER
  4. Reload the current user’s group assignments.

    newgrp -

Greeter

On elementary OS 5.1, there’s a bit of a glitch after installing libvirt on the system. That is, a new libvirt-qemu user appears as a logon option in Greeter. This isn’t supposed to happen but luckily there’s a workaround. The steps here hide the libvirt-qemu login in Greeter. The steps were come from this solution on Stack Overflow.

  1. Set the libvirt-qemu user account as a system account for the accountsservices package to hide it in the login menu.

    printf "[User]\nSystemAccount=true\n" \
      | sudo tee /var/lib/AccountsService/users/libvirt-qemu
  2. Restart the accounts service.

    sudo systemctl restart accounts-daemon.service

Btrfs

If you use Btrfs on your system like I do, then you’ll want to avoid CoW on CoW when using virtual machine disk images. Using the default CoW qcow2 format for virtual disk images on top of a Btrfs filesystem is asking for trouble. This section demonstrates the various ways of disabling CoW for virtual disk images on Btrfs filesystems. If you snapshot your filesystem, take care to place virtual disk images in a subvolume that is excluded from snapshots. Snapshots for virtual disk images should be handled in the disk image itself as is the case with the qcow2 format. At least, that’s the way until a Btrfs storage driver appears for libvirt. I can hope.

qemu-img

When creating a qcow2 image directly with qemu-img(1), the nocow option can be used to disable CoW for that file. The following command creates a 25 gigabyte qcow2 image named my-vm-image.qcow2 with CoW disabled.

qemu-img create -o nocow my-vm-image.qcow2 25G

libvirt Storage Pool Features

In libvirt 6.6.0, Storage Pool Features were introduced, including the cow feature. This version of libvirt disabled CoW by default on Btrfs filesystems. This default behavior was quickly rescinded in libvirt 6.7.0 which re-enabled CoW by default. The change leaves the decision to disable CoW in the hands of system administrators. If your lucky enough to be using libvirt 6.6.0 or newer, you can take advantage of this feature.

elementary OS 5.1 and Ubuntu 18.04 only ship with access to libvirt 4.0.0 Even if you use the virtualization PPA, it only goes up to version 4.7.0 for Ubuntu 18.04. You’ll need to get newer version by some external means or use a newer version of Ubuntu for this to work.

libvirt uses the concept of storage pools to abstract the complexities involved in managing the underlying virtual machine disk images in a variety of situations. I won’t delve into the details here. Refer to Storage Management for more information. For the purposes of this post you should know that libvirt’s default directory for disk images is its default storage pool. This pool is a simple Directory pool. libvirt stores pretty much all configuration in XML files. This is the case for storage pools and the XML can be viewed and edited with virsh(1). The steps here walk through the steps to disable CoW on the default storage pool.

  1. List storage pools with the pool-list subcommand. The default pool is just called default. No surprises here.

    virsh pool-list
     Name                 State      Autostart
    -------------------------------------------
     default              active     yes
  2. To simply view the XML, use the pool-dumpxml subcommand followed by the pool’s name. Here I output the default pool’s XML configuration where you can see that path is indeed /var/lib/libvirt/images.

    virsh pool-dumpxml default
    <pool type='dir'>
      <name>default</name>
      <uuid>4f779eae-e312-4e4d-bf9f-fafe0e334f63</uuid>
      <capacity unit='bytes'>1999372288000</capacity>
      <allocation unit='bytes'>191017480192</allocation>
      <available unit='bytes'>1808354807808</available>
      <source>
      </source>
      <target>
        <path>/var/lib/libvirt/images</path>
        <permissions>
          <mode>0755</mode>
          <owner>0</owner>
          <group>0</group>
        </permissions>
      </target>
    </pool>
  3. Edit a pool’s configuration with the pool-edit subcommand. To modify the default pool’s XML, the command would appear as follows.

    virsh pool-edit default
  4. To disable CoW, set the cow feature with state=no in the pool’s XML.

    The snippet here demonstrates the XML to disable CoW.

    <features>
      <cow state='no'>
    </features>

    For the default storage pool, the resulting XML to disable CoW could appear like so.

    <pool type='dir'>
      <name>default</name>
      <uuid>4f779eae-e312-4e4d-bf9f-fafe0e334f63</uuid>
      <capacity unit='bytes'>1999372288000</capacity>
      <allocation unit='bytes'>191017480192</allocation>
      <available unit='bytes'>1808354807808</available>
      <features>
        <cow state='no'>
      </features>
      <source>
      </source>
      <target>
        <path>/var/lib/libvirt/images</path>
        <permissions>
          <mode>0755</mode>
          <owner>0</owner>
          <group>0</group>
        </permissions>
      </target>
    </pool>

chattr

The simplest way to disable CoW on a particular directory or file is with chattr(1) as described in Can copy-on-write be turned off for data blocks?. To do this, add the no copy on write attribute with the +C option. The following commands disable CoW on libvirt’s image directory.

Disable CoW on the /var/lib/libvirt/images directory.

sudo chattr +C /var/lib/libvirt/images

A Flat Layout Subvolume

A dedicated Btrfs subvolume for /var/lib/libvirt/images is probably your best option since it excludes the disk images from snapshots. The subvolume can have CoW disabled via chattr, but CoW can also be disabled with the mount option nodatacow when using a subvolume in a flat layout. The steps here create a dedicated subvolume for libvirt’s disk image directory and mount it with CoW disabled.

  1. Mount the root Btrfs filesystem to create a subvolume.

    sudo mount (df --output=source / | tail -n 1) /mnt
  2. Create a dedicated Btrfs subvolume for libvirt’s virtual disk images.

    sudo btrfs subvolume create /mnt/var-lib-libvirt-images
    Create subvolume '/mnt/var-lib-libvirt-images'
  3. Add the subvolume to fstab(5).

    echo (df --output=source / \
      | tail -n 1)" /var/lib/libvirt/images btrfs defaults,nodatacow,noatime,subvol=var-lib-libvirt-images 0 0" \
      | sudo tee -a /etc/fstab
    /dev/mapper/sda2_crypt /var/lib/libvirt/images btrfs defaults,nodatacow,noatime,subvol=var-lib-libvirt-images 0 0
  4. Verify there are no errors in fstab.

    sudo findmnt --verify --verbose
  5. Now mount the subvolume according to the rule just added in fstab.

    sudo mount /var/lib/libvirt/images
  6. Don’t forget to unmount /mnt.

    sudo umount /mnt

That’s it! The default storage pool for libvirt will store virtual disk images in this subvolume.

virt-manager

virt-manager is an application for managing virtual machines with libvirt graphically. It’s a handy one for the toolbox, though some might prefer the simplicity of Boxes.

Install virt-manager.

sudo apt -y install virt-manager

If you haven’t logged out and back in since installing libvirt, you’ll need to that before running virt-manager.

Conclusion

You should now be able to get virtual machines up and running without issue. Now that you have all the components in place for virtualization, why not make your life easier with Boxes? I’ll cover all the details of installing the GNOME Boxes Flatpak on a Btrfs system in an upcoming post, so stay tuned!