ZFS brings enterprise-grade storage features to KVM that traditional filesystems cannot match – built-in checksumming, snapshots, compression, and self-healing data are just the starting points. When combined with KVM, ZFS provides a storage backend that handles VM disk images with integrity guarantees that ext4 or XFS simply do not offer. This guide covers installing ZFS on Linux, creating a storage pool and dataset for libvirt, defining the pool in virsh, and attaching ZFS-backed volumes to virtual machines.
Why Use ZFS for KVM Storage
KVM’s default storage approach uses qcow2 files on a local filesystem. That works, but ZFS adds several capabilities that matter for virtualization:
- Copy-on-write snapshots are instant and space-efficient – ideal for VM snapshots
- Data checksumming detects and corrects silent corruption (bit rot)
- Transparent compression (LZ4 or ZSTD) saves disk space without performance penalties
- Clones let you create new VMs from snapshots without copying data
- Send/receive enables replication to remote hosts for disaster recovery
Prerequisites
- A KVM host running Ubuntu 24.04 or RHEL 10
- At least one dedicated disk or partition for ZFS (e.g.,
/dev/sdb) - Root or sudo access
- libvirt and QEMU/KVM already installed
Step 1 – Install ZFS on Linux
Ubuntu 24.04
ZFS is available in the Ubuntu repositories. Install the kernel module and userspace tools.
sudo apt update
sudo apt install -y zfsutils-linux
Verify the ZFS module is loaded.
lsmod | grep zfs
zfs --version
RHEL 10 / AlmaLinux 10 / Rocky Linux 10
On RHEL-based distributions, install ZFS from the official ZFS on Linux repository.
sudo dnf install -y https://zfsonlinux.org/epel/zfs-release-2-4.el10.noarch.rpm
sudo dnf install -y kernel-devel
sudo dnf install -y zfs
Load the ZFS kernel module and enable auto-loading at boot.
sudo modprobe zfs
echo "zfs" | sudo tee /etc/modules-load.d/zfs.conf
Verify the installation.
zfs --version
zpool version
Step 2 – Create a ZFS Pool
Create a ZFS pool using one or more disks. For a single-disk setup (lab or non-critical workloads).
sudo zpool create kvmpool /dev/sdb
For a mirrored setup (recommended for production – survives a single disk failure).
sudo zpool create kvmpool mirror /dev/sdb /dev/sdc
Verify the pool was created.
zpool status kvmpool
zpool list
Expected output shows the pool online with all devices healthy.
Step 3 – Create a ZFS Dataset for libvirt
Create a dedicated dataset within the pool for KVM virtual machine storage. Enable compression and set a reasonable record size for VM workloads.
sudo zfs create kvmpool/libvirt
sudo zfs set compression=lz4 kvmpool/libvirt
sudo zfs set recordsize=64K kvmpool/libvirt
The 64K record size is a good balance for VM disk I/O patterns. LZ4 compression is fast enough that it rarely causes a performance hit and often improves throughput by reducing the amount of data written to disk.
Verify the dataset.
zfs list
zfs get compression,recordsize kvmpool/libvirt
The dataset is automatically mounted at /kvmpool/libvirt. Set the correct ownership for libvirt.
sudo chown libvirt-qemu:kvm /kvmpool/libvirt
Step 4 – Define a libvirt Storage Pool
Define a directory-based storage pool in libvirt that points to the ZFS dataset mount point. While libvirt has a native ZFS pool type, using a directory pool on a ZFS mount is simpler and gives you the same benefits.
virsh pool-define-as zfs-pool dir --target /kvmpool/libvirt
Start the pool and set it to auto-start on boot.
virsh pool-start zfs-pool
virsh pool-autostart zfs-pool
Verify the pool is active.
virsh pool-list --all
virsh pool-info zfs-pool
Step 5 – Create a Volume in the Pool
Create a disk image for a virtual machine within the ZFS-backed pool.
virsh vol-create-as zfs-pool myvm-disk.qcow2 20G --format qcow2
Verify the volume was created.
virsh vol-list zfs-pool
virsh vol-info myvm-disk.qcow2 --pool zfs-pool
You can also verify the file on disk.
ls -lh /kvmpool/libvirt/
qemu-img info /kvmpool/libvirt/myvm-disk.qcow2
Step 6 – Attach the Volume to a VM
When creating a new VM, reference the pool and volume.
virt-install \
--name myvm \
--ram 2048 \
--vcpus 2 \
--disk vol=zfs-pool/myvm-disk.qcow2,bus=virtio \
--os-variant ubuntu24.04 \
--network bridge=br0 \
--cdrom /kvmpool/libvirt/ubuntu-24.04.iso
For an existing VM, attach the disk with virsh attach-disk.
virsh attach-disk myvm /kvmpool/libvirt/myvm-disk.qcow2 vdb --driver qemu --subdriver qcow2 --persistent
Verify the disk is attached.
virsh domblklist myvm
Taking ZFS Snapshots of VM Disks
One of the biggest advantages of ZFS is instant snapshots. Create a snapshot of the entire libvirt dataset before making changes.
sudo zfs snapshot kvmpool/libvirt@before-upgrade
List snapshots.
zfs list -t snapshot
Roll back to a snapshot if something goes wrong (VM must be shut down first).
virsh shutdown myvm
sudo zfs rollback kvmpool/libvirt@before-upgrade
virsh start myvm
Create a clone from a snapshot to spin up a new VM quickly.
sudo zfs clone kvmpool/libvirt@before-upgrade kvmpool/libvirt-clone
Monitoring ZFS Pool Health
Regularly check pool status for disk errors or degraded states.
zpool status kvmpool
zpool iostat kvmpool 5
Run a scrub periodically to verify data integrity.
sudo zpool scrub kvmpool
zpool status kvmpool
Summary
ZFS provides a storage backend for KVM that goes well beyond what traditional filesystems offer. The combination of checksumming, compression, instant snapshots, and clones makes it a strong choice for both lab environments and production hypervisors. Setting it up is straightforward – install ZFS, create a pool and dataset, define a libvirt storage pool pointing to the ZFS mount, and start creating VM volumes. The snapshot and clone capabilities alone make the setup worthwhile, giving you fast backup and restore options that work at the filesystem level without needing external tools.