Part of the InventiveHQ KVM virtualization series. Complete Episodes 1–3 first so
demo-ubuntu01exists and is running.
The operating-system disk is only one part of a virtual machine. Databases, application data, backups, and logs often belong on separate volumes with their own capacity and lifecycle. Libvirt storage pools give those volumes a consistent management layer without hiding the underlying files or devices.
In this episode we create a directory-backed pool named tutorial-data, allocate a 5 GiB qcow2 volume, attach it to the running guest as vdb, format and mount it, then expand it to 8 GiB without stopping the VM.
Lab verified July 2026 · libvirt 12.0.0 · QEMU 10.2.1 · online ext4 expansion
Use the command builder to review storage and domain inspection commands before changing disk definitions.
Finished storage layout
| Resource | Value |
|---|---|
| Pool name | tutorial-data |
| Pool type | Directory |
| Pool target | /var/lib/libvirt/tutorial-data |
| Volume | demo-data01.qcow2 |
| Initial capacity | 5 GiB |
| Final capacity | 8 GiB |
| Guest target | vdb using virtio |
| Filesystem | ext4 labeled demo-data |
| Guest mount | /srv/demo-data |
| Scope | Live and persistent attachment |
1. Inventory before creating storage
Set the system connection and inspect existing resources:
export LIBVIRT_DEFAULT_URI=qemu:///system
VM=demo-ubuntu01
virsh uri
virsh pool-list --all
virsh domblklist "$VM" --details
Refuse to reuse names or paths that already exist:
virsh pool-info tutorial-data 2>/dev/null && {
echo "tutorial-data already exists; inspect it first"
exit 1
}
sudo test ! -e /var/lib/libvirt/tutorial-data/demo-data01.qcow2
domblklist --details is especially important. A guest target such as vdb must not already point to another disk.
2. Define a persistent directory pool
Create the backing directory:
sudo mkdir -p /var/lib/libvirt/tutorial-data
Define, build, start, and enable the pool at boot:
virsh pool-define-as \
tutorial-data \
dir \
--target /var/lib/libvirt/tutorial-data
virsh pool-build tutorial-data
virsh pool-start tutorial-data
virsh pool-autostart tutorial-data
virsh pool-info tutorial-data
pool-define-as creates a persistent definition. That differs from pool-create-as, which creates a transient pool that disappears from libvirt after it stops.
The pool reports the capacity and free space of the filesystem containing its target directory. It does not reserve all of that capacity for one guest.
3. Create a qcow2 volume
Create the initial 5 GiB volume:
virsh vol-create-as \
tutorial-data \
demo-data01.qcow2 \
5GiB \
--format qcow2
Inspect both the logical capacity and current allocation:
virsh vol-info \
--pool tutorial-data \
demo-data01.qcow2
virsh vol-path \
--pool tutorial-data \
demo-data01.qcow2
A new sparse qcow2 volume can have 5 GiB of guest-visible capacity while consuming only a few hundred KiB on the host.
4. Attach the disk live and persistently
Save the volume path and attach it as the next virtio disk:
DATA_DISK=$(virsh vol-path \
--pool tutorial-data \
demo-data01.qcow2)
virsh attach-disk \
"$VM" \
"$DATA_DISK" \
vdb \
--targetbus virtio \
--subdriver qcow2 \
--live \
--config
Verify the result rather than assuming the target name:
virsh domblklist "$VM" --details
virsh domblkinfo "$VM" vdb
virsh dumpxml "$VM" --inactive > "${VM}-with-data-disk.xml"
--live changes the running guest. --config updates its persistent definition so the disk returns after reboot.
5. Identify and format the new device inside Ubuntu
Connect to the guest and inventory block devices:
lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINTS
sudo blkid /dev/vdb || true
Stop if vdb already contains a filesystem or data you did not create. Formatting the wrong target is irreversible.
For this empty tutorial volume, create ext4 directly on the whole device:
sudo mkfs.ext4 -L demo-data /dev/vdb
sudo mkdir -p /srv/demo-data
sudo mount /dev/vdb /srv/demo-data
echo "InventiveHQ KVM storage lab" | \
sudo tee /srv/demo-data/README.txt
df -hT /srv/demo-data
sudo cat /srv/demo-data/README.txt
The validated 5 GiB device produced an approximately 4.9 GiB ext4 filesystem after filesystem metadata and reserved space.
For a durable production mount, use the filesystem UUID in /etc/fstab, test it with mount -a, and retain console access before rebooting. Device names such as vdb can change when hardware definitions change.
6. Expand an attached active disk
An intuitive first attempt is:
virsh vol-resize \
--pool tutorial-data \
demo-data01.qcow2 \
8GiB
In the running lab this failed safely because QEMU held the qcow2 write lock:
qemu-img: Could not open 'demo-data01.qcow2':
Failed to get "write" lock
For the disk attached to the active domain, resize the domain block device:
virsh blockresize "$VM" vdb 8GiB
virsh domblkinfo "$VM" vdb
virsh vol-info \
--pool tutorial-data \
demo-data01.qcow2
Both commands then reported 8 GiB capacity.
7. Grow the ext4 filesystem online
Inside the guest, confirm that the kernel sees the larger block device:
lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINTS /dev/vdb
Because this tutorial put ext4 directly on /dev/vdb, grow it online:
sudo resize2fs /dev/vdb
df -hT /srv/demo-data
sudo cat /srv/demo-data/README.txt
The mounted filesystem grew from approximately 4.9 GiB to 7.8 GiB, and the validation file remained intact.
If your disk contains a partition table, grow the correct partition before the filesystem. LVM, XFS, encrypted volumes, and other layouts require their own tools and ordering.
Verification checklist
# On the KVM host
virsh pool-info tutorial-data
virsh vol-info --pool tutorial-data demo-data01.qcow2
virsh domblklist demo-ubuntu01 --details
virsh domblkinfo demo-ubuntu01 vdb
# Inside the guest
lsblk -f /dev/vdb
findmnt /srv/demo-data
df -hT /srv/demo-data
sudo cat /srv/demo-data/README.txt
The episode is complete only when the pool is persistent and autostarting, the disk exists in both live and inactive domain definitions, the filesystem is 8 GiB-class, and its data remains readable.
Safe detach procedure
Do not detach a mounted filesystem. If the disk must be removed later:
# Inside the guest
sudo sync
sudo umount /srv/demo-data
# On the host
virsh detach-disk demo-ubuntu01 vdb --live --config
virsh domblklist demo-ubuntu01 --details
Detaching a device does not necessarily delete its storage volume. Confirm backups and dependencies before virsh vol-delete.
Official references
- libvirt storage pool and volume XML
- virsh storage, volume, and blockresize commands
- Ubuntu Server libvirt documentation
Continue to Episode 5: Create a custom KVM NAT network with static DHCP.