<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Lvm · XB Field Notes</title><link>https://www.xbcatm.com/tags/lvm/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://www.xbcatm.com/tags/lvm/index.xml" rel="self" type="application/rss+xml"/><item><title>LVM snapshots capture a block device in time</title><link>https://www.xbcatm.com/posts/lvm-logical-volume-snapshot/</link><pubDate>Mon, 10 Apr 2028 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/posts/lvm-logical-volume-snapshot/</guid><description>A logical volume snapshot preserves the original blocks, letting you back up or roll back a partition without taking it offline.</description><content:encoded>&lt;p&gt;An LVM snapshot is neither a copy nor a reflink: it is a fixed-size copy-on-write exception store attached to an origin logical volume. &lt;code&gt;lvcreate -s -L 5G -n snap vg/data&lt;/code&gt; allocates 5 GiB from the volume group, and the device-mapper &lt;code&gt;snapshot&lt;/code&gt; target records the original contents of each origin chunk the first time it is written. Creation is instant whatever the origin&amp;rsquo;s size, because nothing is copied.&lt;/p&gt;
&lt;p&gt;That size is the whole safety margin, and it is the parameter people get wrong. A store sized for a quiet week is exhausted in an hour by a database checkpoint or an &lt;code&gt;rsync&lt;/code&gt;, and when it fills the snapshot does not degrade — it becomes invalid and returns I/O errors. The recovery point disappears exactly when you needed it, which is why the store&amp;rsquo;s free space belongs on a dashboard, not in a runbook.&lt;/p&gt;
&lt;h2 id="an-exception-store-not-a-copy"&gt;An exception store, not a copy&lt;/h2&gt;
&lt;p&gt;A classic LVM snapshot uses two device-mapper targets: the origin is re-mapped as &lt;code&gt;snapshot-origin&lt;/code&gt;, and a new &lt;code&gt;snapshot&lt;/code&gt; device combines it with a COW device. Reads of unchanged chunks go straight to the origin; the first write to a chunk copies the old chunk into the COW device, then writes the new data. So consumption tracks the number of distinct chunks that changed, not the number of writes: the simulation writes a million times to sixteen hot chunks and consumes fifteen exceptions, about 60 KiB.&lt;/p&gt;
&lt;p&gt;Watch it with &lt;code&gt;lvs -a -o lv_name,lv_attr,origin,data_percent,lv_size&lt;/code&gt;. A classic snapshot&amp;rsquo;s &lt;code&gt;lv_attr&lt;/code&gt; reads &lt;code&gt;swi-a-s---&lt;/code&gt; — &lt;code&gt;s&lt;/code&gt; for snapshot, &lt;code&gt;w&lt;/code&gt; writable, and a state character that becomes &lt;code&gt;I&lt;/code&gt; when the store is exhausted. &lt;code&gt;data_percent&lt;/code&gt; is the store&amp;rsquo;s fill, and that is the number to alert on, at 70 or 80 percent; 100 percent is not a warning, it is an obituary. &lt;code&gt;lvextend&lt;/code&gt; grows the COW device while the snapshot is still valid; &lt;code&gt;lvchange --monitor y&lt;/code&gt; lets dmeventd do it automatically.&lt;/p&gt;
&lt;h2 id="exhaustion-is-invalidation"&gt;Exhaustion is invalidation&lt;/h2&gt;
&lt;p&gt;The kernel documentation is blunt: the COW device will often be smaller than the origin, and if it fills up the snapshot becomes useless and is disabled, returning errors. LVM&amp;rsquo;s guidance is to plan for expected writes — often 20 percent of the origin — and extend before it fills. A small slice of the store holds the COW block map, so usable data capacity is a little under the &lt;code&gt;-L&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;The blast radius is narrow but the timing is cruel: the origin keeps running normally, so nothing looks wrong until the backup that reads the snapshot fails or the rollback is unavailable. There is no partial mode: at 100 percent, the snapshot never existed.&lt;/p&gt;
&lt;h2 id="merging-and-why-thin-changes-the-failure"&gt;Merging, and why thin changes the failure&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lvconvert --merge vg/snap&lt;/code&gt; rolls the snapshot&amp;rsquo;s changed chunks back into the origin, and it runs in the background while the origin stays usable — writes to chunks not yet merged are deferred. The constraint is on the origin: it must be inactive when the merge is set up, so unmount it, or schedule the merge and activate the origin afterwards. Merging consumes the snapshot, so copy out anything you need first.&lt;/p&gt;
&lt;p&gt;Thin snapshots change the failure mode rather than removing it. A thin snapshot is created when the origin is a thin LV and no size is given; it shares blocks in the thin pool instead of owning a store, so its own divergence cannot invalidate it. The pool is the shared resource now: when it runs out, the damage is not one dead snapshot but every thin volume in the pool, which LVM warns may be damaged and hard to repair. &lt;code&gt;thin_pool_autoextend_threshold&lt;/code&gt; and dmeventd exist precisely because pool exhaustion is a pool-wide outage.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;The classic failure is a backup that silently stops being a backup. The job mounts the snapshot, copies for longer than the store can cover, and the store reaches 100 percent; the snapshot goes invalid and the copy fails — or worse, monitoring watches only the exit code while the job reads a stale mount. Check &lt;code&gt;data_percent&lt;/code&gt; on every snapshot LV, alert at 70 percent, and size for the busiest hour in the retention window.&lt;/p&gt;
&lt;p&gt;The second failure is the merge that cannot happen when you need it. &lt;code&gt;lvconvert --merge&lt;/code&gt; requires the origin closed, and teams discover that during the incident. Rehearse the merge on a disposable volume, keep a real backup independent of the snapshot, and remember that a snapshot whose store is full cannot be merged.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;Look at the store&amp;rsquo;s fill before trusting the snapshot:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;lvs -a -o lv_name,vg_name,lv_attr,origin,data_percent,lv_size
lvs -a -o lv_name,data_percent,metadata_percent vg/thinpool
dmsetup status vg-snap
lvextend -L +2G vg/snap
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;data_percent&lt;/code&gt; is the COW store&amp;rsquo;s fill and should sit well under 100; a state character of &lt;code&gt;I&lt;/code&gt; in &lt;code&gt;lv_attr&lt;/code&gt; means the snapshot is already invalid. For a thin snapshot, &lt;code&gt;data_percent&lt;/code&gt; and &lt;code&gt;metadata_percent&lt;/code&gt; on the pool are the numbers that matter; &lt;code&gt;dmsetup status&lt;/code&gt; shows allocated sectors against the total so you can watch a merge drain.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/lvcreate.8.html"&gt;lvcreate(8)&lt;/a&gt;, documents &lt;code&gt;-s&lt;/code&gt;/&lt;code&gt;--snapshot&lt;/code&gt;, COW versus thin snapshots and the sizing guidance, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/lvs.8.html"&gt;lvs(8)&lt;/a&gt;, defines the &lt;code&gt;lv_attr&lt;/code&gt; state characters including invalid snapshot, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.kernel.org/admin-guide/device-mapper/snapshot.html"&gt;Device-mapper snapshot support&lt;/a&gt;, the exception store, invalidation and &lt;code&gt;lvconvert --merge&lt;/code&gt; behaviour, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man7/lvmthin.7.html"&gt;lvmthin(7)&lt;/a&gt;, thin snapshots, pool exhaustion and automatic extension, checked 2026-09-16.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>