How To

How to Configure a Disk Storage Pool in KVM

A disk storage pool hands libvirt a whole block device and lets it manage the partition table. Every volume you create becomes a real partition on that device, which is why this pool type behaves differently from every other one on the list: you do not get to name the volumes, and you cannot ask for qcow2.

Original content from computingforgeeks.com - post 89908

This guide configures a KVM disk storage pool with virsh from an empty device through to a formatted filesystem inside a guest, including the two errors that stop most people on the first attempt. The pool type suits dedicated spindles or LUNs where the partition table is the allocation unit and raw block throughput matters more than snapshots. Commands were run on Ubuntu 26.04 with libvirt 12.0 in August 2026, against a 20 GB virtio device. If libvirt is not installed yet, the KVM hypervisor setup covers that first.

Prerequisites

You need a dedicated block device with nothing on it that you care about. Building the pool writes a fresh partition table and destroys whatever was there. A second virtual disk, a spare SSD, or an attached LUN all work; a partition does not, because a disk pool wants the whole device.

Confirm which device is free before touching anything. The root disk here is vda and the spare is vdb:

lsblk -o NAME,SIZE,TYPE,MOUNTPOINT

An unpartitioned device shows as a bare disk with no children and nothing mounted:

NAME      SIZE TYPE MOUNTPOINT
vda       200G disk
├─vda1  198.9G part /
├─vda13  1023M part /boot
├─vda15   106M part /boot/efi
vdb        20G disk
vdc        10G disk

Sizing follows the guests, not libvirt. A disk pool allocates every volume in full at creation time with no thin provisioning anywhere in the path, so the device must physically hold the sum of the volumes you intend to carve out. Plan the device at the total committed size plus room to grow, not at expected usage.

Step 1: Define the pool

Four things go into the definition: a pool name, the type, the source device, and the partition-table format. GPT is the sensible default because MSDOS caps you at four primary partitions, which means four volumes:

sudo virsh pool-define-as kvm-disk-pool disk \
  --source-dev /dev/vdb \
  --source-format gpt \
  --target /dev

The target is /dev because the volumes appear there as device nodes rather than as files in a directory. libvirt confirms the definition without touching the disk yet:

Pool kvm-disk-pool defined

Step 2: Build and start the pool

Building is the destructive step. It writes the partition table onto the device, and --overwrite tells libvirt to proceed even if a filesystem signature is already present. Check the device name once more before running it:

sudo virsh pool-build kvm-disk-pool --overwrite
sudo virsh pool-start kvm-disk-pool
sudo virsh pool-autostart kvm-disk-pool

Autostart matters more here than on a directory pool. A pool that does not come back after a host reboot leaves every guest that references it unable to start:

Pool kvm-disk-pool built
Pool kvm-disk-pool started
Pool kvm-disk-pool marked as autostarted

The pool reports the full device capacity with nothing allocated:

sudo virsh pool-info kvm-disk-pool

Capacity matches the device and allocation sits at zero until the first volume is carved out:

Name:           kvm-disk-pool
UUID:           bb509ee8-e704-4136-95d0-9184eee9a66f
State:          running
Persistent:     yes
Autostart:      yes
Capacity:       20.00 GiB
Allocation:     0.00 B
Available:      20.00 GiB

Step 3: Create volumes

Two rules apply here that do not apply to any file-backed pool, and both surface as hard errors rather than warnings. Volumes take the next sequential partition name on the device:

sudo virsh vol-create-as kvm-disk-pool vdb1 8G
sudo virsh vol-create-as kvm-disk-pool vdb2 6G

Each one becomes a partition immediately:

Vol vdb1 created
Vol vdb2 created

Listing with --details shows what actually got made. Type is block, and allocation equals capacity on every row:

sudo virsh vol-list kvm-disk-pool --details

Nothing is thin here. An 8 GiB volume consumes 8 GiB the moment it exists:

 Name   Path        Type    Capacity   Allocation
---------------------------------------------------
 vdb1   /dev/vdb1   block   8.00 GiB   8.00 GiB
 vdb2   /dev/vdb2   block   6.00 GiB   6.00 GiB
virsh pool-define-as, pool-build and vol-create-as output showing a KVM disk storage pool and its partition volumes

The host sees the same layout through parted, which is a useful cross-check that libvirt and the kernel agree on the table:

Number  Start   End     Size    File system  Name     Flags
 1      17.4kB  8590MB  8590MB  ext4         primary
 2      8590MB  15.0GB  6442MB               primary

Error: “invalid partition name ‘webdata’, expected ‘vdb3′”

Volume names in a disk pool are not yours to choose. libvirt derives them from the device and the next free partition slot, and rejects anything else:

error: Failed to create vol webdata
error: invalid argument: invalid partition name 'webdata', expected 'vdb3'

The error is helpful because it names the string it wants. Use exactly that. If descriptive names for each guest’s storage are a requirement, a disk pool is the wrong type and a logical volume pool gives you arbitrary names on the same block-device model.

Error: “unknown volume format type raw”

Passing a file format fails, and the message misleads people into thinking raw is unsupported entirely:

error: Failed to create vol vdb1
error: unsupported configuration: unknown volume format type raw

The --format flag on a disk pool means partition type, not image format. Valid values are the partition-table entries such as none, linux, linux-swap, linux-lvm, linux-raid and extended. Omitting the flag works and is what the commands above do:

sudo virsh vol-create-as kvm-disk-pool vdb3 4G --format linux

Storing a qcow2 image is not possible on this pool type at all, because a volume is a partition rather than a file. Copy-on-write, backing files and internal snapshots all need a file-backed pool, so reach for a directory pool when those matter.

Step 4: Attach a volume to a guest

Volumes attach by device path. The --persistent flag writes the change into the domain XML so it survives a shutdown rather than vanishing with the running instance:

sudo virsh attach-disk web01 /dev/vdb1 vdb --targetbus virtio --persistent

Check the domain’s block devices to confirm libvirt wired it to the target you asked for:

sudo virsh domblklist web01

The partition appears alongside the guest’s own qcow2 root disk:

 Target   Source
--------------------------------------------------
 vda      /var/lib/libvirt/images/web01.qcow2
 vdb      /dev/vdb1
 sda      /var/lib/libvirt/images/web01-seed.iso

Inside the guest it is an unformatted 8 GB disk. Put a filesystem on it and mount it as you would any new block device:

sudo mkfs.ext4 -L vmdata /dev/vdb
sudo mkdir -p /srv/data
sudo mount /dev/vdb /srv/data
df -hT /srv/data

The guest reports the usable size after filesystem overhead, slightly under the 8 GiB that was allocated:

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/vdb       ext4  7.8G   24K  7.4G   1% /srv/data
virsh attach-disk and domblklist output attaching a disk pool partition to a running KVM guest

Mount it by UUID in /etc/fstab rather than by /dev/vdb. Attaching another disk later can renumber the device nodes and a path-based entry then mounts the wrong volume or fails the boot. Read the UUID with sudo blkid /dev/vdb.

Step 5: Delete a volume

Deleting removes the partition from the table. There is no undo and no recycle step, so detach it from any guest first:

sudo virsh vol-delete vdb3 --pool kvm-disk-pool
sudo virsh vol-list kvm-disk-pool

The remaining volumes keep their numbering, and the freed space returns to the pool:

Vol vdb3 deleted

 Name   Path
-------------------
 vdb1   /dev/vdb1
 vdb2   /dev/vdb2

Deleting from the middle leaves a gap that libvirt tracks as a free extent in the pool XML. Because partition numbering is sequential, that gap is only reusable by a volume that fits inside it, which is how a disk pool ends up fragmented after a few cycles of create and delete:

<source>
  <device path='/dev/vdb'>
    <freeExtent start='15032402944' end='21474819584'/>
  </device>
  <format type='gpt'/>
</source>

When a disk pool is the wrong choice

The constraints above are not bugs. A disk pool is a thin wrapper over a partition table, and a partition table cannot express names, thin provisioning or snapshots. Those limits decide the pool type more often than performance does.

RequirementDisk poolUse instead
Named volumes per guestNo, names forced to vdbNLVM pool
Thin provisioningNo, full allocationLVM thin, or directory pool with qcow2
SnapshotsNoLVM pool, ZFS pool
qcow2 and backing filesNo, volumes are partitionsDirectory pool
Shared across hostsNoNFS pool, iSCSI pool
Raw block throughput, no layersYesDisk pool

The case where a disk pool wins is a dedicated device handed to a small, stable set of guests where you want the shortest possible path from guest to platter and nothing clever in between. For everything else the siblings fit better: NFS and iSCSI pools for shared storage across hosts, and a ZFS pool where checksums and snapshots are worth the RAM. Day-to-day pool and volume management from the CLI is covered in the virsh command reference, and the same pools are manageable from a browser through WebVirtCloud.

Keep reading

Install KVM and Virt-Manager on Arch Linux Virtualization Install KVM and Virt-Manager on Arch Linux Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift Best NVMe SSD for Proxmox: Endurance and Power-Loss Protection Proxmox Best NVMe SSD for Proxmox: Endurance and Power-Loss Protection Best NAS Case for a DIY Build: ITX to 12-Bay Chassis Storage Best NAS Case for a DIY Build: ITX to 12-Bay Chassis How To Install Amazon Linux 2023 on KVM using QCOW2 Image KVM How To Install Amazon Linux 2023 on KVM using QCOW2 Image

Leave a Comment

Press ESC to close