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.qcow2The same process can be repeated for other VMs.
Current Situation Before Moving the VM
First, list the existing VMs:
sudo virsh list --allExample 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 runningIn this example, I will move the client VM first.
Check the disk used by the VM:
sudo virsh domblklist client --detailsExample output:
Type Device Target Source
----------------------------------------------------------------
file disk vda /var/lib/libvirt/images/client.qcow2The VM currently uses this disk image:
/var/lib/libvirt/images/client.qcow2The goal is to move it to:
/mnt/data/libvirt/images/client.qcow2- Confirm the External Disk Is Mounted
First, confirm that the external disk is mounted:
findmnt /mnt/data
df -h /mnt/dataExample output:
TARGET SOURCE FSTYPE OPTIONS
/mnt/data /dev/sda1 ext4 rw,relatime,stripe=8191Filesystem Size Used Avail Use% Mounted on
/dev/sda1 916G 2.1M 870G 1% /mnt/dataIn this case, the external disk is mounted at:
/mnt/dataIt 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.
- 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/fstabIf there is no entry, get the UUID of the partition:
sudo blkid /dev/sda1Then edit /etc/fstab:
sudo vi /etc/fstabAdd an entry similar to this:
UUID=your-disk-uuid /mnt/data ext4 defaults,nofail,x-systemd.device-timeout=10 0 2Replace your-disk-uuid with the real UUID from blkid.
Then test the mount:
sudo mount -a
df -h /mnt/dataDo 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.
- Create a Directory for VM Images
Create a directory for VM disk images on the external disk:
sudo mkdir -p /mnt/data/libvirt/imagesSet 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/imagesThis allows QEMU/libvirt to access the VM disk images.
- 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-storageVerify the storage pools:
sudo virsh pool-list --all
sudo virsh pool-info usb-vm-storageExample output:
Name State Autostart
--------------------------------------
images active yes
usb-vm-storage active yesName: 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 GiBAt 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.
- Check the Existing VM Disk Path
Check the disk used by the VM you want to move:
sudo virsh domblklist client --detailsExample output:
Type Device Target Source
----------------------------------------------------------------
file disk vda /var/lib/libvirt/images/client.qcow2This confirms that the client VM is still using the original disk image under:
/var/lib/libvirt/images/- Check for Snapshots or Backing Files
Before moving a qcow2 file, check whether the VM has snapshots:
sudo virsh snapshot-list clientExample 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.qcow2If 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.
- Shut Down the VM
Gracefully shut down the VM:
sudo virsh shutdown clientCheck the VM state:
sudo virsh domstate clientExpected output:
shut offNow check the qcow2 file again:
sudo qemu-img info --backing-chain /var/lib/libvirt/images/client.qcow2Example 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 GiBIn 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.
- 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.qcow2Refresh the storage pool:
sudo virsh pool-refresh usb-vm-storageConfirm that the file exists:
ls -lh /mnt/data/libvirt/images/client.qcow2
sudo virsh vol-list usb-vm-storage- Back Up the VM XML
Before changing the VM configuration, back up the current XML:
sudo virsh dumpxml client > ~/client-before-move.xmlThis gives you a copy of the original VM configuration in case you need to compare or restore it later.
- Update the VM Disk Path
Edit the VM configuration:
sudo virsh edit clientFind 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#gThen save and quit:
:wq- Confirm the VM Uses the New Disk Path
After saving the XML, check the VM disk path again:
sudo virsh domblklist client --detailsExpected output:
Type Device Target Source
-----------------------------------------------------------------
file disk vda /mnt/data/libvirt/images/client.qcow2This confirms that the VM now points to the disk image on the new storage pool.
- Start the VM
Start the VM:
sudo virsh start clientCheck the state:
sudo virsh domstate clientExpected output:
runningCheck the disk path again:
sudo virsh domblklist client --detailsExample output:
Type Device Target Source
-----------------------------------------------------------------
file disk vda /mnt/data/libvirt/images/client.qcow2After 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