LFCE 145 š§ KVM Storage ā Pools and Volumes
KVM storage is managed through libvirt’s storage pool abstraction. A pool is a named source of storageāa directory, a physical disk, an LVM volume group, an NFS export, or an iSCSI target. A volume is a single virtual disk image within that pool. The pool defines where the storage lives; the volume is the file or block device that gets attached to a virtual machine. Without a pool, libvirt has no organized way to allocate, track, or attach disks to guests.
This chapter covers the pool types that matter for LFCE administration, the virsh commands that define and start pools, the volume creation commands for directory-based pools, and the qcow2 versus raw format decision that determines whether snapshots and thin provisioning are available.
Key point: A directory pool is the simplest and most common type. It stores volumes as files in a host directory, and the volume format (qcow2 or raw) determines whether the disk supports snapshots and sparse allocation. An LVM or iSCSI pool presents block devices, and libvirt cannot create volumes in the same way.
Why pools and volumes are separate
The abstraction problem. A virtual machine needs a disk. The disk can be a file in /var/lib/libvirt/images, an LVM logical volume, a partition on a raw device, or a LUN on an iSCSI target. Without a pool abstraction, every VM definition would need to know the exact path or device name, and moving a VM to a different storage backend would require editing the VM’s XML. The pool gives the storage a name, and the VM references the pool and volume name instead of the underlying path.
The capacity problem. A pool knows how much space it has, how much is allocated, and how much is available. virsh pool-info reports those numbers. A directory pool reports the filesystem’s free space. An LVM pool reports the volume group’s free extents. This is the information an administrator needs before creating a new volume or migrating a VM.
The lifecycle problem. A pool has a defined state: defined, built, started, and autostarted. The virsh pool-define-as command creates the configuration. virsh pool-build creates the target directory or initializes the source device. virsh pool-start activates the pool so libvirt can use it. virsh pool-autostart ensures the pool is available after a host reboot . Without the start step, a defined pool is invisible to VM creation.
a. The directory pool
The directory pool is the default and the easiest to understand. The pool’s target is a host directory, and every volume is a file inside it. libvirt creates the directory if it does not exist, and the pool reports the filesystem’s capacity and allocation.
# Define a persistent directory pool
virsh pool-define-as guest_images dir --target "/guest_images"
# Build the pool (creates the target directory)
virsh pool-build guest_images
# Start the pool
virsh pool-start guest_images
# Enable autostart
virsh pool-autostart guest_images
After the pool is started, virsh pool-list --all shows it as active, and virsh pool-info guest_images reports its state and capacity . The pool definition is stored in /etc/libvirt/storage/, and the autostart symlink is placed in /etc/libvirt/storage/autostart/ .
The default pool on most libvirt installations is a directory pool targeting /var/lib/libvirt/images. It is active and autostarted by default.
b. Creating volumes
A volume in a directory pool is a disk image file. The virsh vol-create-as command creates it with a name, a capacity, and optionally an allocation and format.
# Create a 10 GB volume with default format
virsh vol-create-as --pool guest_images --name vm1.qcow2 --capacity 10G
# Create a sparse qcow2 volume
virsh vol-create-as --pool guest_images --name vm2.qcow2 \
--capacity 20G --allocation 0 --format qcow2
The capacity is the virtual size of the disk as the guest sees it. The allocation is the physical size on the host. When allocation is less than capacity, the volume is sparse: the host file starts small and grows as the guest writes data . This is thin provisioning at the file level, and it is how qcow2 images avoid consuming the full virtual size immediately.
The virsh vol-info command shows the capacity and allocation of an existing volume. A sparse volume shows a small allocation and a large capacity .
An alternative is to create the volume from an XML definition, which allows specifying the target path, permissions, and format explicitly. The virsh vol-create command takes the pool and the XML file .
c. qcow2 versus raw
The volume format determines the features available. The qcow2 format supports snapshots, backing files, compression, and encryption. The raw format is a direct representation of the disk contents with no metadata, and it is faster for I/O but does not support the advanced features .
| Feature | qcow2 | raw |
|---|---|---|
| Snapshots | Yes | No (without external tools) |
| Backing files | Yes | No |
| Sparse allocation | Yes | Yes (if filesystem supports it) |
| Performance | Lower (metadata overhead) | Higher |
| Transfer size | Smaller (only written sectors) | Full size |
A qcow2 volume is the standard choice for a VM that will be snapshotted, cloned, or used as a template. A raw volume is the choice for a VM with an I/O-intensive workload where snapshots are not needed and the image will not be transferred over the network .
The backing file feature is what makes qcow2 useful for templates. A base image is created and marked read-only, and new volumes are created with the base as a backing file. The new volume stores only the changes, and the base is shared across all the VMs created from it. This is the mechanism behind cloning and snapshot chains .
Complete Example Session
# ============================================
# PART 1: LIST EXISTING POOLS
# ============================================
virsh pool-list --all
# NAME STATE AUTOSTART
# default active yes
# ============================================
# PART 2: DEFINE A DIRECTORY POOL
# ============================================
virsh pool-define-as guest_images dir --target "/guest_images"
# ============================================
# PART 3: BUILD AND START THE POOL
# ============================================
virsh pool-build guest_images
virsh pool-start guest_images
virsh pool-autostart guest_images
# ============================================
# PART 4: VERIFY THE POOL
# ============================================
virsh pool-info guest_images
# Name: guest_images
# UUID: ...
# State: running
# Persistent: yes
# Autostart: yes
# Capacity: ...
# Allocation: ...
# Available: ...
# ============================================
# PART 5: CREATE A VOLUME
# ============================================
virsh vol-create-as --pool guest_images --name vm1.qcow2 \
--capacity 10G --format qcow2
# ============================================
# PART 6: VERIFY THE VOLUME
# ============================================
virsh vol-info --pool guest_images vm1.qcow2
# Name: vm1.qcow2
# Type: file
# Capacity: 10.00 GiB
# Allocation: 196.00 KiB
# ============================================
# PART 7: CREATE A SPARSE VOLUME
# ============================================
virsh vol-create-as --pool guest_images --name vm2.qcow2 \
--capacity 20G --allocation 0 --format qcow2
# ============================================
# PART 8: LIST VOLUMES IN THE POOL
# ============================================
virsh vol-list --pool guest_images
# Name Path
# vm1.qcow2 /guest_images/vm1.qcow2
# vm2.qcow2 /guest_images/vm2.qcow2
# ============================================
# PART 9: THE DEFAULT POOL
# ============================================
virsh vol-list --pool default
# The default pool targets /var/lib/libvirt/images.
# ============================================
# PART 10: REMOVE A POOL
# ============================================
virsh pool-destroy guest_images
virsh pool-delete guest_images
virsh pool-undefine guest_images
The ten parts covered listing pools, defining a directory pool, building and starting it, verifying the pool, creating a volume, verifying the volume, creating a sparse volume, listing volumes, the default pool, and removing a pool.
Quick Reference
Pool lifecycle commands
| Command | Purpose |
|---|---|
virsh pool-define-as <name> <type> --target <path> | Create a persistent pool definition |
virsh pool-build <name> | Create the target directory or initialize the source |
virsh pool-start <name> | Activate the pool |
virsh pool-autostart <name> | Start on host boot |
virsh pool-list --all | List all pools |
virsh pool-info <name> | Show pool details |
virsh pool-destroy <name> | Stop the pool |
virsh pool-delete <name> | Delete the target directory (must be empty) |
virsh pool-undefine <name> | Remove the configuration |
Pool types
| Type | Source | Volume creation |
|---|---|---|
dir | Host directory | File per volume |
fs | Formatted filesystem | File per volume |
disk | Physical disk/partition | Partition per volume |
logical | LVM volume group | Logical volume per volume |
netfs | NFS export | File per volume |
iscsi | iSCSI target | LUN per volume (pre-existing) |
Volume commands
| Command | Purpose |
|---|---|
virsh vol-create-as --pool <p> --name <n> --capacity <size> | Create a volume |
virsh vol-create <pool> <xml> | Create from XML |
virsh vol-list --pool <p> | List volumes |
virsh vol-info --pool <p> <name> | Show volume details |
virsh vol-clone <name> <newname> --pool <p> | Clone a volume |
virsh vol-delete --pool <p> <name> | Delete a volume |
Format comparison
| Format | Snapshots | Backing files | Performance |
|---|---|---|---|
qcow2 | Yes | Yes | Lower |
raw | No | No | Higher |
Best Practices
ā Do This:
# Use a directory pool for most workloads
virsh pool-define-as images dir --target /var/lib/libvirt/images # ā
# Use qcow2 for VMs that will be cloned or snapshotted
virsh vol-create-as --pool images --name vm.qcow2 --format qcow2 # ā
# Autostart the pool so it survives a reboot
virsh pool-autostart images # ā
# Check capacity and allocation before creating volumes
virsh pool-info images # ā
ā Don’t Do This:
# Create a volume without starting the pool
virsh vol-create-as --pool images ... # pool must be active # ā
# Use raw format for a VM that needs snapshots
virsh vol-create-as ... --format raw # ā
# Delete a pool's directory without destroying the pool first
rm -rf /guest_images # ā
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
vol-create-as fails | Pool not started | virsh pool-start <name> |
| Pool not available after reboot | Autostart not set | virsh pool-autostart <name> |
| Volume allocation equals capacity | Sparse allocation not requested | Set --allocation 0 |
| Snapshot fails | Volume format is raw | Recreate the volume as qcow2 |
pool-delete fails | Directory not empty | Remove volumes first |
| LVM pool has no volumes | libvirt cannot create LVs on all pool types | Create the LV manually or use a directory pool |
Real-World Examples
1. Define a directory pool
virsh pool-define-as images dir --target /var/lib/libvirt/images
2. Build and start
virsh pool-build images && virsh pool-start images
3. Autostart
virsh pool-autostart images
4. Create a qcow2 volume
virsh vol-create-as --pool images --name web.qcow2 --capacity 20G --format qcow2
5. Create a sparse volume
virsh vol-create-as --pool images --name db.qcow2 --capacity 50G --allocation 0 --format qcow2
6. List volumes
virsh vol-list --pool images
7. Volume info
virsh vol-info --pool images web.qcow2
8. Clone a volume
virsh vol-clone --pool images web.qcow2 web-clone.qcow2
9. Delete a volume
virsh vol-delete --pool images web-clone.qcow2
10. Remove a pool
virsh pool-destroy images && virsh pool-undefine images
Visual
The pool and volume hierarchy
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā STORAGE POOL: guest_images (dir) ā
ā Target: /guest_images ā
ā ā
ā āāā vm1.qcow2 (volume, 10G capacity) ā
ā āāā vm2.qcow2 (volume, 20G capacity, sparse) ā
ā āāā template.qcow2 (volume, base image) ā
ā ā
ā Each volume is a file in the pool's target directory. ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
The pool lifecycle
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā pool-define-as āāāŗ configuration created ā
ā ā ā
ā ā¼ ā
ā pool-build āāāŗ target directory created ā
ā ā ā
ā ā¼ ā
ā pool-start āāāŗ pool active, volumes can be created ā
ā ā ā
ā ā¼ ā
ā pool-autostart āāāŗ active after reboot ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
qcow2 backing files
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā base.qcow2 (read-only, shared) ā
ā ā ā
ā āāā vm1.qcow2 (backing file: base.qcow2) ā
ā ā āāā stores only vm1's changes ā
ā ā ā
ā āāā vm2.qcow2 (backing file: base.qcow2) ā
ā āāā stores only vm2's changes ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Sparse allocation
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā capacity: 20G allocation: 0 ā
ā ā ā ā
ā ā¼ ā¼ ā
ā Guest sees 20G Host file starts near 0 ā
ā Grows as guest writes ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Summary
| Item | Value |
|---|---|
| Pool abstraction | Named storage source managed by libvirt |
| Volume abstraction | Single disk image within a pool |
| Directory pool command | virsh pool-define-as <name> dir --target <path> |
| Pool start | virsh pool-start <name> |
| Volume create | virsh vol-create-as --pool <p> --name <n> --capacity <size> |
| Sparse volume | --allocation 0 |
| qcow2 features | Snapshots, backing files, compression |
| raw features | Performance, no metadata |
| Pool configuration location | /etc/libvirt/storage/ |
| Autostart symlink | /etc/libvirt/storage/autostart/ |
Key takeaways:
- A pool is a named storage source; a volume is a disk image within it. The pool abstracts the underlying directory, LVM group, or iSCSI target. The VM references the pool and volume name, not the raw path .
- The directory pool is the default and the most flexible. It stores volumes as files in a host directory, and libvirt can create and delete volumes directly. The
virsh pool-define-as,pool-build,pool-start, andpool-autostartcommands create a persistent, active pool . qcow2is the format for snapshots and thin provisioning. It supports backing files, which allow a base image to be shared across multiple VMs.rawis faster but lacks those features .- Sparse allocation is how
qcow2avoids consuming the full virtual size. Theallocationis the physical size; thecapacityis the size the guest sees. Setting allocation to zero creates a thin-provisioned volume that grows as data is written . - The pool must be started before volumes can be created. A defined pool is a configuration, not an active resource.
virsh pool-startactivates it, andvirsh pool-autostartensures it is active after a reboot. - The default pool targets
/var/lib/libvirt/images. It is a directory pool, active and autostarted on most installations. Use it for simple deployments, and create additional pools when the storage needs to be separated by workload or backend.
Remember: KVM storage is organized as pools and volumes. The pool defines where the storage lives, and the volume is the disk that gets attached to the guest. A directory pool is the simplest and most flexible. A qcow2 volume supports snapshots and thin provisioning; a raw volume prioritizes I/O performance. The pool lifecycle is define, build, start, and autostart. Without the start step, the pool is invisible to VM creation. Without the autostart step, the pool disappears after a reboot.
Stop using slow, ad-bloated tool sites! š¤®
š Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
ā
Finance (Mortgage, Interest, Inflation)
ā
Tech (Base64, JSON, Dev Suite, IP)
ā
Health (BMI, BMR, TDEE)
ā
Productivity (Timer, Workspace, QR)
ā”ļø Fast & Private
š No data leaves your device
š 100% Free
š Use it now: https://tools.kandz.me
š Bookmark itāyouāll need it later!