LVM Thin Provisioning: Over-Committing Safely
- Explain thin provisioning: virtual size up front, blocks allocated on write, and why a pool can be over-committed
- Build a thin pool from a data LV and a metadata LV, and read the two hidden volumes behind it
- Read pool health from lvs — Attr flags plus Data% and Meta% as two independent ways to run out
- Know what a full pool actually does to reads and writes, and the auto-extend defenses that keep it from happening
- Use thin snapshots and understand why they replace classic copy-on-write snapshots
The one sentence that defines thin provisioning
Understand virtual size, allocate-on-write, and over-commit
lvm-guidebuilt a normal logical volume:lvcreate -L 50Gcarves 50 GB out of the pool the moment you run it, whether you ever write a byte or not. The space is spent on day one — the same day-one guess raw partitions forced on you, just one layer up.Thin provisioning breaks that too. A thin logical volume is given a virtual size when you create it, but it consumes real blocks from its pool only as data is actually written. Create a 200 GB thin LV on a 100 GB pool and nothing is wrong: it reports 200 GB, and the pool hands out real blocks as writes arrive. The consequence is the headline: the virtual sizes of all the thin LVs in a pool can legitimately add up to more than the physical space behind them. That is over-commitment, and it is a feature, not an accident.
The mechanism is worth stating precisely because the whole lesson hangs on it. The
lvmthin(7)manual puts it plainly: "blocks in a thin provisioned LV are allocated as they are written… a thin provisioned LV is given a virtual size, and can then be much larger than physically available storage." So a thin LV is a promise to allocate later, backed by a shared reservoir of real blocks called the thin pool. As long as the reservoir has blocks left, every promise is honoured. The engineering — and the risk — is entirely about what happens when it doesn't.Check yourself
You create a 500 GB thin LV on a thin pool that has 100 GB of real space, then write 40 GB to it. How much of the pool is consumed, and is anything wrong?
A thin pool is two LVs wearing a trench coat
Build the pool from a data LV and a metadata LV, and see the hidden volumes
A thin pool is not a single object. It is two logical volumes doing different jobs: a large data LV that actually holds the blocks written to every thin volume, and a small metadata LV that records which block belongs to which thin LV (and to which snapshot). You can let LVM build both in one command, or fuse two LVs you made yourself — the second form is worth seeing once, because it is what the one-shot is doing for you.
Example — not runnable on your lab box
sudo lvcreate --type thin-pool -L 100M -n thinpool datavgThin pool volume with chunk size 64.00 KiB can address at most 15.81 TiB of data. Logical volume "thinpool" created.
- --type thin-pool
- build a thin pool (data LV + metadata LV) rather than a normal LV
- -L 100M
- size of the pool's DATA LV — the real capacity every thin volume draws from
The one-shot: LVM carves the data LV, sizes and carves a metadata LV automatically, and fuses them into a pool named thinpool. From here on you create thin volumes in the pool, never straight out of the VG. This block is a demo — it needs a real VG with free space, so it runs in the paired lab, not here. Example — not runnable on your lab box
sudo lvconvert --type thin-pool --poolmetadata datavg/pool_meta datavg/pool_dataConverted datavg/pool_data to thin pool.
- --type thin-pool
- the target type for the conversion
- --poolmetadata VG/LV
- the LV to use as pool metadata; the named data LV becomes the pool
The long way, and the reason the pieces exist: make a data LV and a metadata LV yourself, then lvconvert fuses them. After conversion the two originals become hidden and are renamed thinpool_tdata and thinpool_tmeta — you address the pool by its new name, never the parts. Knowing this is what makes the next command's output readable. Example — not runnable on your lab box
sudo lvs -a -o +devices datavgLV VG Attr LSize Pool Data% Meta% thinpool datavg twi-aotz-- 100.00m 0.00 10.55 [thinpool_tdata] datavg Twi-ao---- 100.00m [thinpool_tmeta] datavg ewi-ao---- 4.00m
- -a
- show hidden/internal LVs (the _tdata / _tmeta parts, snapshots' internals)
- -o +devices
- append a column to the default set rather than replacing it
The -a (all) flag reveals the internal volumes normal lvs hides. thinpool is the pool you use; the bracketed [thinpool_tdata] and [thinpool_tmeta] are the two LVs it's made of. Seeing all three at once is what makes the model click — a pool is data plus metadata, and each is a volume you could, in principle, run out of. Check yourself
In
lvs -a, why do the_tdataand_tmetavolumes appear in square brackets, and which one would you extend to give thin LVs more room to grow?Read pool health: two independent ways to run out
Interpret Attr, Data% and Meta% before you trust a pool
lvm-guidetaught you to readlvsper layer. Thin pools add two columns you must read together, because they fail independently. TheAttrstring tells you what a row is: a leading `t` marks a thin pool, a leading `V` marks a thin volume. ThenData%is how full the pool's data reservoir is, andMeta%is how full its metadata is — and a pool can be half-empty on data and completely out of metadata at the same time. Metadata exhaustion is the failure nobody sees coming, because they were watching the big number.Example — not runnable on your lab box
sudo lvs -o lv_name,lv_attr,lv_size,data_percent,metadata_percent datavgLV Attr LSize Data% Meta% thinpool twi-aotz-- 100.00m 82.14 38.90 app0 Vwi-aotz-- 200.00m 41.07
- data_percent
- on a pool row: fraction of the DATA reservoir used — writes fail when it hits 100
- metadata_percent
- fraction of the METADATA LV used — a second, independent way to wedge the pool
Two rows, two Attr shapes. thinpool leads with t — it's the pool, 82% full on data, 39% on metadata. app0 leads with V — it's a thin volume of 200 MiB virtual, and its own Data% (41%) is how much of the pool THIS volume is responsible for. The pool row is the one that decides whether the machine is about to have a very bad day: 82% and climbing is a pool that needs runway now. Check yourself
lvsshows a pool atData% 34.0andMeta% 99.2. Writes to its thin volumes are failing. What's happening?What 'full' actually does — the payoff
Predict a full pool's behaviour and its blast radius
Here is the fact that makes over-commit survivable instead of reckless. When a thin pool's data space is exhausted, reads keep working — the blocks already written are still there and still mapped. It is writes that fail, because there is no free block to allocate.
lvmthin(7): "the errorwhenfull setting applies only to writes; reading thin LVs can continue even when data space is exhausted." Whether a write fails loudly (an immediate I/O error) or hangs waiting for space is a per-pool setting,--errorwhenfull y|n, chosen when the pool is created or changed.Check yourself
A thin pool hits 100% data usage. A monitoring service reads its config from a thin LV on that pool; a logging service writes to another. Which one breaks, and why?
The defenses, in the order you'd reach for them
Keep a pool from ever filling — monitoring, auto-extend, and its failure modes
You do not manage a thin pool by hoping. LVM ships a monitoring daemon,
dmeventd, that watches every activated pool and acts before full. Perlvmthin(7), it emits syslog warnings at 80%, 85%, 90% and 95% pool usage, and — if configured — auto-extends the pool from the VG's free space so it never reaches 100%. Twolvm.confsettings govern it:thin_pool_autoextend_threshold(the percent-full that triggers an extension; 100 disables auto-extend, and the minimum useful value is 50) andthin_pool_autoextend_percent(how much to add each time, as a percentage of current size).Example — not runnable on your lab box
sudo lvextend -L +100M datavg/thinpoolSize of logical volume datavg/thinpool_tdata changed from 100.00 MiB to 200.00 MiB. Logical volume datavg/thinpool successfully resized.
- -L +100M
- grow the pool's data space BY 100 MiB from VG free space (the + matters)
- --poolmetadatasize +N
- the sibling knob: grow the METADATA LV when Meta% is the one climbing
The manual extend — what auto-extend automates, and what you run yourself when a pool is climbing and you don't want to wait for a daemon. Note the output: it grew thinpool_tdata, the data reservoir, from the VG's free space. Data% halves the instant this lands, because the same used blocks are now a smaller fraction of a bigger pool. Freeing space by deleting thin LVs is NOT a reliable substitute — only blocks unshared with any snapshot come back. Check yourself
A pool crossed 95%,
dmeventdis running, and monitoring is enabled — but it never auto-extended and writes are now failing. What's the most likely missing precondition?Thin snapshots — why they replaced the classic kind
Take thin snapshots and know what makes them cheap
Thin pools change what a snapshot costs. A classic copy-on-write snapshot (from
lvm-guide) reserves a fixed chunk of space up front and is dropped if the origin changes more than that reservation — and a snapshot of a snapshot compounds the overhead. A thin snapshot is just another thin volume in the same pool that starts out sharing all of the origin's blocks. Nothing is copied at creation; blocks diverge only when either side is written.lvmthin(7): snapshots of thin LVs "share common data blocks with their origin," snapshots may be taken of snapshots, and "there is no limit to or degradation from sequences of snapshots."Example — not runnable on your lab box
sudo lvcreate -s -n app0_snap datavg/app0Logical volume "app0_snap" created.
- -s
- snapshot of the given origin; against a thin LV it creates a thin snapshot in the same pool
- -n app0_snap
- the snapshot's name — it appears as its own V-attr thin volume in lvs
No -L. A classic snapshot needed a size for its copy-on-write store; a thin snapshot draws from the pool like any thin volume, so there's nothing to size and nothing to overflow independently — it can only consume pool space that diverges from the origin. app0_snap starts near-free and grows only as app0 or the snapshot is written. The catch is the flip side of the gift: every snapshot is one more consumer of the same pool, so snapshots are now a pool-capacity decision, not a free lunch. Check yourself
Why can a thin snapshot be created instantly with no size argument, when a classic LVM snapshot needed both a size and careful change-rate math?
Now do it — read a pool's health on the box
Walk the read commands that front every thin-pool task
The write commands need a real pool (that's the paired lab). But the reads are the first thirty seconds of every thin-pool task you'll ever do, and they run anywhere. Walk them for real: the point is to build the reflex of reading
Data%andMeta%together before you trust — or touch — a pool. On a box with no LVM these print empty and still exit cleanly; the muscle memory is the goal.sudo lvs -o lv_name,lv_attr,lv_size,data_percent,metadata_percentThe health read. Scan the Attr column for a leading t (a pool) or V (a thin volume), then read Data% and Meta% on every pool row — out loud, both numbers. If you can say 'pool X is N% data, M% metadata' from this output, you can triage a thin-pool page at 2 a.m. sudo lvs -aThe -a view exposes the [_tdata] and [_tmeta] internals behind each pool. Seeing the pieces is what keeps the two-numbers habit honest: data and metadata are separate volumes, and either one filling stops the pool. sudo vgsStraight from lvm-guide, and doubly load-bearing here: VFree is the space auto-extend (or your own lvextend) has to grow a climbing pool into. A pool near full over a VG with zero free space has no runway — read this before you assume the pool can save itself. sudo dmsetup lsThe device-mapper view underneath LVM: a thin pool shows up as its -tpool, -tdata and -tmeta targets, and each thin volume as its own thin target. When lvs and reality seem to disagree, this is the ground truth of what the kernel actually has mapped.
- Thin provisioning gives an LV a virtual size and allocates real blocks only on write, so the thin LVs in a pool can over-commit — promise more space than physically exists — and the one new job that buys you is watching the pool.
- A thin pool is two LVs: a data LV (the block reservoir) and a metadata LV (block ownership), built with lvcreate --type thin-pool or fused with lvconvert; lvs -a shows the hidden _tdata/_tmeta parts.
- Read Data% AND Meta% together — a pool can be empty on data and full on metadata, and either at 100% stops allocation. Attr leads with t for a pool, V for a thin volume.
- A full pool fails writes but still serves reads (--errorwhenfull sets loud-vs-hang); the fix is runway — lvextend the pool's data (or --poolmetadatasize the metadata) from VG free space.
- dmeventd warns at 80/85/90/95% and can auto-extend, but only if it's running+monitored, the threshold is under 100, and the VG has free space; thin snapshots share the origin's blocks (instant, no size), so prune them deliberately.
| Task | Command |
|---|---|
| Read pool health | lvs -o lv_name,lv_attr,data_percent,metadata_percent |
| See the pool's internals | lvs -a (the [_tdata] / [_tmeta] parts) |
| Create a thin pool | lvcreate --type thin-pool -L 100M -n thinpool datavg |
| Provision a thin volume | lvcreate -V 200M -T datavg/thinpool -n app0 |
| Give the pool runway | lvextend -L +100M datavg/thinpool (data) · --poolmetadatasize +N (metadata) |
| Thin snapshot | lvcreate -s -n app0_snap datavg/app0 (no size) |
| Ground truth underneath | dmsetup ls (the -tpool / -tdata / -tmeta targets) |
