Add a New KVM Storage Pool and Move Existing VMs to It on Ubuntu 24.04

In a previous post, I showed how to set up KVM and Cockpit on Ubuntu 24.04.

That setup works well for creating and managing virtual machines from a browser. However, after running several VMs, the host system disk can fill up quickly.

In my case, my existing VMs were using the default libvirt image directory:

/var/lib/libvirt/images/

Later, I added an external USB hard disk and wanted to move some existing VM disk images to it.

This post shows how to create a new libvirt storage pool on the external disk and move an existing KVM VM disk image from the default storage location to the new storage pool.


Prerequisite: Existing KVM and Cockpit Setup

This post assumes that KVM, libvirt, and Cockpit are already installed and working on the Ubuntu host.

If you have not set up KVM and Cockpit yet, follow my previous post first:

How to Set Up KVM and Cockpit on Ubuntu 24.04

In that setup, VM disk images are stored in the default libvirt image directory:

/var/lib/libvirt/images/

This post continues from that environment and focuses only on adding a new storage pool and moving existing VM disk images to it.


Environment

In this example, I am using:

Host OS: Ubuntu 24.04
Virtualization: KVM / QEMU / libvirt
Management UI: Cockpit
External disk mount point: /mnt/data
New storage pool name: usb-vm-storage
VM name: client
Original VM disk: /var/lib/libvirt/images/client.qcow2
New VM disk path: /mnt/data/libvirt/images/client.qcow2

The same process can be repeated for other VMs.


Current Situation Before Moving the VM

First, list the existing VMs:

sudo virsh list --all

Example output:

Id   Name          State
-----------------------------
17   harbor        running
18   zabbix        running
25   jenkins       running
28   artifactory   running
30   osm           running
33   mumble        running
34   proxy         running
35   client        running

In this example, I will move the client VM first.

Check the disk used by the VM:

sudo virsh domblklist client --details

Example output:

Type   Device   Target   Source
----------------------------------------------------------------
file   disk     vda      /var/lib/libvirt/images/client.qcow2

The VM currently uses this disk image:

/var/lib/libvirt/images/client.qcow2

The goal is to move it to:

/mnt/data/libvirt/images/client.qcow2

  1. Confirm the External Disk Is Mounted

First, confirm that the external disk is mounted:

findmnt /mnt/data
df -h /mnt/data

Example output:

TARGET    SOURCE    FSTYPE OPTIONS
/mnt/data /dev/sda1 ext4   rw,relatime,stripe=8191
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       916G  2.1M  870G   1% /mnt/data

In this case, the external disk is mounted at:

/mnt/data

It is also formatted as ext4, which is suitable for storing KVM qcow2 images.

I do not recommend storing active VM disk images on NTFS or exFAT. For KVM/libvirt on Linux, use a Linux filesystem such as ext4 or xfs.


  1. Make Sure the External Disk Is Persistently Mounted

Before using the disk for VM storage, make sure it is mounted automatically after reboot.

Check /etc/fstab:

grep /mnt/data /etc/fstab

If there is no entry, get the UUID of the partition:

sudo blkid /dev/sda1

Then edit /etc/fstab:

sudo vi /etc/fstab

Add an entry similar to this:

UUID=your-disk-uuid /mnt/data ext4 defaults,nofail,x-systemd.device-timeout=10 0 2

Replace your-disk-uuid with the real UUID from blkid.

Then test the mount:

sudo mount -a
df -h /mnt/data

Do not continue until the disk mounts correctly.

If the disk is not mounted after reboot, any VM stored on it will not be able to start.


  1. Create a Directory for VM Images

Create a directory for VM disk images on the external disk:

sudo mkdir -p /mnt/data/libvirt/images

Set the owner and permissions:

sudo chown -R libvirt-qemu:kvm /mnt/data/libvirt
sudo chmod 755 /mnt/data
sudo chmod 755 /mnt/data/libvirt
sudo chmod 770 /mnt/data/libvirt/images

This allows QEMU/libvirt to access the VM disk images.


  1. Create a New libvirt Storage Pool

Create a new directory-based libvirt storage pool:

sudo virsh pool-define-as usb-vm-storage dir --target /mnt/data/libvirt/images
sudo virsh pool-build usb-vm-storage
sudo virsh pool-start usb-vm-storage
sudo virsh pool-autostart usb-vm-storage

Verify the storage pools:

sudo virsh pool-list --all
sudo virsh pool-info usb-vm-storage

Example output:

Name             State    Autostart
--------------------------------------
images           active   yes
usb-vm-storage   active   yes
Name:           usb-vm-storage
UUID:           a569e69a-cdb7-4a5a-b414-5dbeda4b292a
State:          running
Persistent:     yes
Autostart:      yes
Capacity:       915.82 GiB
Allocation:     2.04 MiB
Available:      915.81 GiB

At this point, libvirt can use the external disk as a storage pool.

The original default pool remains unchanged, and the new pool is available separately.


  1. Check the Existing VM Disk Path

Check the disk used by the VM you want to move:

sudo virsh domblklist client --details

Example output:

Type   Device   Target   Source
----------------------------------------------------------------
file   disk     vda      /var/lib/libvirt/images/client.qcow2

This confirms that the client VM is still using the original disk image under:

/var/lib/libvirt/images/

  1. Check for Snapshots or Backing Files

Before moving a qcow2 file, check whether the VM has snapshots:

sudo virsh snapshot-list client

Example output:

Name   Creation Time   State
-------------------------------

An empty list means there are no libvirt snapshots.

Next, check whether the qcow2 file has a backing chain:

sudo qemu-img info --backing-chain /var/lib/libvirt/images/client.qcow2

If the VM is still running, you may see this error:

qemu-img: Could not open '/var/lib/libvirt/images/client.qcow2': Failed to get shared "write" lock
Is another process using the image [/var/lib/libvirt/images/client.qcow2]?

This is normal when the VM is running. The disk image is locked because QEMU is using it.

Shut down the VM before checking the image and copying it.


  1. Shut Down the VM

Gracefully shut down the VM:

sudo virsh shutdown client

Check the VM state:

sudo virsh domstate client

Expected output:

shut off

Now check the qcow2 file again:

sudo qemu-img info --backing-chain /var/lib/libvirt/images/client.qcow2

Example output:

image: /var/lib/libvirt/images/client.qcow2
file format: qcow2
virtual size: 150 GiB (161061273600 bytes)
disk size: 144 GiB
cluster_size: 65536
Format specific information:
    compat: 1.1
    compression type: zlib
    lazy refcounts: false
    refcount bits: 16
    corrupt: false
    extended l2: false
Child node '/file':
    filename: /var/lib/libvirt/images/client.qcow2
    protocol type: file
    file length: 144 GiB (155106541568 bytes)
    disk size: 144 GiB

In this example, there is no backing file line, so the VM uses a single qcow2 file.

If you see a backing file, do not blindly copy only the top qcow2 file. You need to handle the whole backing chain.


  1. Copy the VM Disk to the New Storage Pool

Copy the disk image to the new location:

sudo rsync -aHAX --sparse --progress \
  /var/lib/libvirt/images/client.qcow2 \
  /mnt/data/libvirt/images/

This may take some time depending on the VM disk size and the speed of the external disk.

After copying, fix the ownership:

sudo chown libvirt-qemu:kvm /mnt/data/libvirt/images/client.qcow2

Refresh the storage pool:

sudo virsh pool-refresh usb-vm-storage

Confirm that the file exists:

ls -lh /mnt/data/libvirt/images/client.qcow2
sudo virsh vol-list usb-vm-storage

  1. Back Up the VM XML

Before changing the VM configuration, back up the current XML:

sudo virsh dumpxml client > ~/client-before-move.xml

This gives you a copy of the original VM configuration in case you need to compare or restore it later.


  1. Update the VM Disk Path

Edit the VM configuration:

sudo virsh edit client

Find the original disk source:

<source file='/var/lib/libvirt/images/client.qcow2'/>

Change it to the new path:

<source file='/mnt/data/libvirt/images/client.qcow2'/>

The disk section should look similar to this:

<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2'/>
  <source file='/mnt/data/libvirt/images/client.qcow2'/>
  <target dev='vda' bus='virtio'/>
</disk>

If the VM was created through Cockpit, you may also see Cockpit metadata like this:

<cockpit_machines:install_source>/var/lib/libvirt/images/client.qcow2</cockpit_machines:install_source>

You can update it too:

<cockpit_machines:install_source>/mnt/data/libvirt/images/client.qcow2</cockpit_machines:install_source>

The important part for booting the VM is the disk source under the <devices> section:

<source file='/mnt/data/libvirt/images/client.qcow2'/>

To replace all matching paths in vi, you can use:

:%s#/var/lib/libvirt/images/client.qcow2#/mnt/data/libvirt/images/client.qcow2#g

Then save and quit:

:wq

  1. Confirm the VM Uses the New Disk Path

After saving the XML, check the VM disk path again:

sudo virsh domblklist client --details

Expected output:

Type   Device   Target   Source
-----------------------------------------------------------------
file   disk     vda      /mnt/data/libvirt/images/client.qcow2

This confirms that the VM now points to the disk image on the new storage pool.


  1. Start the VM

Start the VM:

sudo virsh start client

Check the state:

sudo virsh domstate client

Expected output:

running

Check the disk path again:

sudo virsh domblklist client --details

Example output:

Type   Device   Target   Source
-----------------------------------------------------------------
file   disk     vda      /mnt/data/libvirt/images/client.qcow2

After confirming that the VM starts correctly and uses the new disk path, keep the old qcow2 file for a while as a fallback. Do not delete the old disk image until you have verified that the VM boots and works normally.

Did this guide save you time?

Support this site

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top