Skip to content
Menu

Linux Storage9 min read

Thin Provisioning & Snapshots

Allocate on demand, snapshot at scale

Note

Prerequisite: this page assumes the PV / VG / LV model from LVM Fundamentals. Thin volumes and snapshots are built on top of dm-thin — see Device Mapper for the underlying target.

What Thin Provisioning Is

Thin provisioning decouples the size an LV advertises from the capacity it has actually consumed. Multiple thin LVs share a single thin pool; physical blocks are allocated from the pool on first write, not at creation time.

Because the sum of advertised sizes can exceed the pool’s real capacity (over-provisioning), thin provisioning lets you treat workloads statistically: most volumes never come close to their declared size, so a smaller physical pool can back many oversized logical volumes.

Thick (traditional) LV
  • Physical PEs claimed at lvcreate
  • Advertised size = allocated size
  • No over-provisioning possible
  • Snapshots need pre-sized CoW space
  • Simple, predictable, never “fills”
Thin LV
  • No physical blocks claimed until write
  • Advertised (virtual) size ≠ allocated size
  • Over-provisioning is the norm
  • Instant, cheap, writable snapshots
  • Pool can fill — must be monitored

Why thin?

  • Efficient utilisation — VMs / containers that declare 100 GB often use 10–20 GB
  • Instant snapshots — no pre-allocated CoW area, snapshot is just another thin LV that shares blocks
  • Snapshots of snapshots — the dependency graph is a tree, not a single level
  • Block-layer analog of qcow2 — same idea (sparse, CoW) but implemented under the filesystem

Thin Pool Anatomy

A thin pool is actually two LVs hidden behind a single logical entity:

Thin LVs — mythin1, mythin2, snapshot, snap-of-snap…
Thin Pool (dm-thin target) — translates thin LV addresses → pool blocks
Data LV (_tdata) — holds actual data blocks
Metadata LV (_tmeta) — B-tree mapping thin addr → pool addr — CRITICAL
Volume Group (PVs)
  • Data LV (pool_tdata) — the physical capacity of the pool. Every first-write-to-an-address in any thin LV allocates a chunk here.
  • Metadata LV (pool_tmeta) — a B-tree that maps (thin LV id, logical block)pool block. Lose this and you lose every thin LV in the pool.

Warning

Metadata is precious. Size it generously at creation time. Rule of thumb: 0.1% of data LV size, minimum 2 MiB, and LVM imposes a 16 GiB hard max. Metadata can be grown later (lvextend --poolmetadatasize) but avoid the scramble.

Creating a thin pool

console
bash
root@host:~# # Let LVM pick metadata size
root@host:~# lvcreate --type thin-pool -L 500G -n tpool data

root@host:~# # Or be explicit (recommended for any sizable pool)
root@host:~# lvcreate --type thin-pool -L 500G --poolmetadatasize 512M -n tpool data
  Thin pool volume with chunk size 256.00 KiB can address at most 63.25 TiB of data.
  Logical volume "tpool" created.

root@host:~# lvs data/tpool
  LV    VG   Attr       LSize   Pool Data%  Meta%
  tpool data twi-a-tz-- 500.00g       0.00   0.15

Creating thin volumes

console
bash
root@host:~# # -V = virtual size (may exceed pool size), -T names the backing pool
root@host:~# lvcreate -V 100G -T data/tpool -n mythin
  Logical volume "mythin" created.

root@host:~# lvcreate -V 100G -T data/tpool -n mythin2
root@host:~# lvcreate -V 2T   -T data/tpool -n fatbutsparse   # 2 TiB virtual on 500 GiB pool

root@host:~# lvs -o name,lv_size,data_percent,pool_lv data
  LV            LSize   Data%  Pool
  tpool         500.00g  0.00
  mythin        100.00g  0.00   tpool
  mythin2       100.00g  0.00   tpool
  fatbutsparse    2.00t  0.00   tpool

Over-provisioning

The sum of virtual sizes in the example above (100+100+2048 = 2248 GiB) is over 4× the pool’s 500 GiB. That’s legal and often desirable. The deal is simple:

  • As long as actual usage stays under the pool’s capacity, everything works
  • When writes push the pool past 100%, the pool enters the pool-full state and starts failing allocations — see the warning below

Warning

Pool-full is the single most important operational concern with thin pools. When the data LV fills, in-flight writes to any thin LV in the pool fail with EIO. The kernel queues further writes for a grace period (configurable no_space_timeout, default 60 s) and then marks volumes read-only. Filesystems on top may hit a remount-ro state or corruption — an application writing at the moment of exhaustion can lose that write. Metadata exhaustion is just as fatal and has no early warning from the FS side.

Autoextend: Growing the Pool Automatically

LVM can watch pool usage via dmeventd and extend the pool when a configured threshold is crossed — assuming the VG has free PEs available. Configured per-host in /etc/lvm/lvm.conf:

console
bash
root@host:~# grep -E 'thin_pool_autoextend' /etc/lvm/lvm.conf
    thin_pool_autoextend_threshold = 80
    thin_pool_autoextend_percent   = 20
  1. Pool writes

    Thin LVs receive writes, pool data usage climbs

  2. Threshold hit

    Pool data usage crosses 80%

  3. dmeventd fires

    Monitored by lvm2-monitor.service

  4. lvextend +20%

    Pool grows if VG has free PEs

  5. Back to watching

    Loop continues until VG is empty

Warning

Autoextend is a safety net, not a strategy. It requires free PEs in the VG. If the VG is full, autoextend cannot help and you are right back at pool-full risk. Treat autoextend as “buy me time to provision more physical capacity,” and alert long before the VG itself runs out.

Monitoring

There are two independent usage values to watch per pool: data_percent and metadata_percent. Alert on both.

console
bash
root@host:~# lvs -o name,lv_size,data_percent,metadata_percent,pool_lv,lv_attr
  LV       LSize   Data%  Meta%  Pool   Attr
  tpool    500.00g 72.41  3.04          twi-aotz--
  mythin   100.00g 18.12         tpool  Vwi-a-tz--
  mythin2  100.00g  6.51         tpool  Vwi-a-tz--

root@host:~# # Low-level: dmsetup status shows live counters
root@host:~# dmsetup status data-tpool
0 1048576000 thin-pool 234 1560/131072 921600/2000000 ...
                           ^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^
                           meta used/total   data used/total  (in chunks)

Alert thresholds worth codifying

  • data_percent > 80 — warning (autoextend should have triggered; investigate why if not)
  • data_percent > 90 — critical
  • metadata_percent > 80 — warning; grow _tmeta with lvextend --poolmetadatasize +
  • VG VFree low — autoextend won’t save you anymore

Thin Snapshots

A thin snapshot is a new thin LV that shares all existing blocks with the origin by pointing at the same pool addresses in the metadata B-tree. On first write to a shared block (from either origin or snapshot) a new pool block is allocated and the mapping is updated — true copy-on-write at the thin-pool level.

console
bash
root@host:~# lvcreate --snapshot -n mythin_snap data/mythin
  Logical volume "mythin_snap" created.

root@host:~# # snapshots are writable by default
root@host:~# mount /dev/data/mythin_snap /mnt/snap

root@host:~# # snapshots-of-snapshots work the same way
root@host:~# lvcreate --snapshot -n mythin_snap2 data/mythin_snap

Thin vs. traditional snapshots

Property Traditional (dm-snapshot) Thin (dm-thin)
Pre-allocated space Yes — fixed-size CoW store per snapshot No — shares the pool
Creation cost Small but non-zero (allocate CoW LV) Instant — only metadata is written
Origin write overhead Read-modify-write on first change per block Single CoW allocation in the pool, negligible
Writable Read-only by default Writable by default
Snapshot-of-snapshot No (or emulated with significant cost) Native, unlimited depth
Scale A handful, short-lived Hundreds or thousands, long-lived
Failure mode if space exhausted Snapshot invalidated & dropped (origin fine) Pool-full affects all LVs in the pool

Common use cases

  • Consistent backupsfsfreeze the origin filesystem, lvcreate --snapshot, fsfreeze -u, back up the snapshot while the origin keeps serving writes. Remove the snapshot when done.
  • Pre-upgrade rollback — snapshot the root LV, run the upgrade, keep the snapshot for a day, remove on success. If something is broken, lvconvert --merge rolls the origin back to the snapshot on next boot.
  • Cheap dev / test clones — spin up dozens of snapshots of a production copy for QA without paying full storage cost.
console
bash
root@host:~# # freeze → snap → thaw pattern
root@host:~# fsfreeze -f /data
root@host:~# lvcreate --snapshot -n data_backup data/mythin
root@host:~# fsfreeze -u /data
root@host:~# mount -o ro,nouuid /dev/data/data_backup /mnt/snap    # nouuid for xfs
root@host:~# rsync -aHAX /mnt/snap/ backup-host:/srv/backups/data/
root@host:~# umount /mnt/snap && lvremove -f /dev/data/data_backup

Pool Repair

If metadata is suspected corrupt (pool won’t activate, lvchange errors about bad references) the thin_* tools (from thin-provisioning-tools) can check, export, and rebuild the B-tree. Run on a deactivated pool.

console
bash
root@host:~# vgchange -a n data

root@host:~# thin_check /dev/mapper/data-tpool_tmeta
examining superblock
examining devices tree
examining mapping tree
checking space map counts

root@host:~# # Export metadata to XML for inspection or rebuild
root@host:~# thin_dump /dev/mapper/data-tpool_tmeta -o /tmp/thin.xml

root@host:~# # Restore onto a fresh metadata LV
root@host:~# thin_restore -i /tmp/thin.xml -o /dev/mapper/data-tpool_tmeta_new

Tip

LVM’s own lvconvert --repair data/tpool wraps this workflow end to end: it snapshots the metadata, runs thin_check, and swaps in the repaired copy. Prefer it unless you need to hand-edit the XML.

Cache LVs (dm-cache)

A cache LV puts an SSD in front of a slower (HDD) LV transparently. dm-cache sits between the filesystem and the origin LV; hot blocks live on the fast device, cold blocks on the slow one. Promotion / demotion is policy-driven.

console
bash
root@host:~# # VG "data" contains both the slow HDD PV and the fast SSD PV

root@host:~# # Create a cache pool on the SSD
root@host:~# lvcreate --type cache-pool -L 50G --cachemode writeback \
                -n cpool data /dev/nvme0n1

root@host:~# # Attach the cache pool to the slow LV
root@host:~# lvconvert --type cache --cachepool data/cpool data/slowlv

root@host:~# lvs -o name,cache_mode,cache_policy,cache_settings,data_percent
  LV      CacheMode   Policy  Settings  Data%
  slowlv  writeback   smq               12.34
writethrough
  • Every write hits cache and origin synchronously
  • Read acceleration only — write latency is HDD-bound
  • Cache device can fail without data loss
  • Safe default when in doubt
writeback
  • Writes acknowledged once they land on the SSD
  • Full read and write acceleration
  • SSD failure = data loss for dirty blocks
  • Use mirrored/RAID1 cache devices for safety

VDO — Dedup & Compression

VDO (Virtual Data Optimizer) is Red Hat’s kernel layer that adds inline deduplication, compression, and thin provisioning below the filesystem. A VDO volume appears as /dev/mapper/<name> and is typically used as the block device under a filesystem or as an LVM PV.

console
bash
root@host:~# # On RHEL/Alma/Rocky
root@host:~# dnf install vdo kmod-kvdo

root@host:~# vdo create --name=vdo0 --device=/dev/data/backing --vdoLogicalSize=10T
root@host:~# mkfs.xfs -K /dev/mapper/vdo0             # -K avoids fstrim of the sparse device
root@host:~# mount -o discard /dev/mapper/vdo0 /backup

root@host:~# vdostats --human-readable
Device            Size       Used Available Use%   Space saving%
/dev/mapper/vdo0   5.0T       1.2T       3.8T  24%            67%

When VDO earns its keep

  • Backup targets — full backups dedupe heavily across generations
  • VM storage with many similar guest images
  • Container registries / image caches — large duplication between layers

Note

Kernel module status: VDO is distributed as an out-of-tree module (kmod-kvdo) on RHEL-family distros. It is not in mainline Linux as of 2024. On Debian/Ubuntu you’re on your own; consider ZFS with compression/dedup or btrfs with compression instead.

Thin LVM vs. ZFS

ZFS has thin volumes (zvols), unlimited instant snapshots, and send/receive built into the same filesystem. If you can run ZFS, you almost always will. LVM-thin stays relevant when ZFS isn’t an option or you want to keep a traditional filesystem stack.

LVM thin + ext4 / xfs
  • Mainline kernel, no licensing concerns
  • Works with any Linux filesystem on top
  • No block-level checksums — bit rot undetectable
  • No send/receive equivalent (use filesystem-level tools)
  • Pool management is imperative, manual
  • Proxmox, RHEL, Ubuntu defaults and ecosystem
ZFS (zvols + datasets)
  • CDDL license — out-of-tree on Linux (OpenZFS DKMS)
  • Integrated filesystem + volume manager + RAID
  • End-to-end checksums and self-healing on redundant vdevs
  • Native zfs send / zfs receive snapshots stream
  • ARC / L2ARC / SLOG for caching — RAM-hungry
  • TrueNAS, FreeBSD, Proxmox (optional) ecosystem

Tip

Pragmatic rule: if you need data integrity guarantees (bit-rot detection, self-healing), reach for ZFS or btrfs — LVM-thin does not checksum. If you just want flexibility and instant snapshots on top of a traditional stack, LVM-thin is the simplest and most universally available option.

Solidnines — solidnines.com