<?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>Snapshot · XB Field Notes</title><link>https://hk.xbcatm.com/tags/snapshot/</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://hk.xbcatm.com/tags/snapshot/index.xml" rel="self" type="application/rss+xml"/><item><title>LVM snapshots capture a block device in time</title><link>https://hk.xbcatm.com/posts/lvm-logical-volume-snapshot/</link><pubDate>Mon, 10 Apr 2028 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/posts/lvm-logical-volume-snapshot/</guid><description>A classic LVM snapshot preserves changed chunks in a finite COW store; consistent backup and merge availability require separate planning.</description><content:encoded>&lt;p&gt;A classic LVM snapshot uses a 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; requests a 5 GiB store, subject to allocation granularity and available volume-group space. It avoids copying the whole origin at creation, but does not guarantee zero work or instant completion. Thin snapshots use a different shared-pool mechanism.&lt;/p&gt;
&lt;p&gt;The exception store must cover distinct changed chunks and metadata for the entire retention window, including writes during backup. A burst can consume it earlier than a quiet-workload estimate predicts. If a classic store fills, the snapshot becomes invalid and reads fail. Monitor validity and growth early enough to extend or finish the backup; a nominal percentage alone is not a recovery plan.&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;The origin uses the &lt;code&gt;snapshot-origin&lt;/code&gt; target, while &lt;code&gt;snapshot&lt;/code&gt; combines unchanged origin chunks with preserved exceptions. Writes to the snapshot itself go to its COW device. Repeated changes to an already-excepted chunk do not require another original copy for that snapshot. The earlier million-write, sixteen-chunk, fifteen-exception result has no recovered trace; touching all sixteen distinct chunks would require sixteen data exceptions, plus metadata.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lvs -a -o lv_name,lv_attr,origin,data_percent,lv_size&lt;/code&gt; exposes type, state and usage. &lt;code&gt;swi-a-s---&lt;/code&gt; is illustrative, not one universal attribute string; the fifth character I means invalid snapshot. Alert thresholds must leave room for observed write rates and response time. &lt;code&gt;lvextend&lt;/code&gt; needs available VG extents. &lt;code&gt;lvchange --monitor y&lt;/code&gt; enables monitoring, not guaranteed extension: dmeventd, configured autoextend policy and free capacity are also required.&lt;/p&gt;
&lt;h2 id="exhaustion-is-invalidation"&gt;Exhaustion is invalidation&lt;/h2&gt;
&lt;p&gt;Kernel documentation says an exhausted classic COW store is disabled and returns errors. The lvcreate manual&amp;rsquo;s twenty-percent-of-origin command is an example, not a general sizing recommendation. Account for chunk granularity, metadata, workload bursts and the full backup duration. Increasing capacity before exhaustion preserves a valid snapshot; an invalid recovery point must not be treated as complete merely because its LV still exists.&lt;/p&gt;
&lt;p&gt;Origin writes normally continue when a classic snapshot overflows, but this does not guarantee that unrelated origin or storage failures cannot occur. An invalid snapshot is unusable as the recovery point; it did exist and may have supplied an earlier verified backup. Do not confuse current snapshot validity with the separate integrity of a copy already completed elsewhere.&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; merges a persistent COW snapshot back into its origin and consumes the snapshot. It starts immediately when both LVs are closed; otherwise LVM defers it until an activation when both are closed. The origin may serve I/O after merging begins, with affected writes deferred as required. This is not a guarantee of interruption-free rollback, and newer origin data can be discarded.&lt;/p&gt;
&lt;p&gt;A thin snapshot shares data and metadata capacity with other thin LVs instead of owning a separately sized COW store. Data exhaustion may queue writes or return errors according to policy; metadata exhaustion is another failure mode. Such failures can affect multiple volumes and filesystems, but do not imply instant destruction of every volume. Monitor both pool percentages, policy and extension capacity.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;A block snapshot is not automatically an application-consistent backup. Filesystem and database buffers, logging and data spread across volumes require coordinated handling and recovery. During copying, check snapshot validity and propagate read failures; verify the resulting contents and a restore. A successful command exit or mount alone does not establish completeness, and this review performed no LVM creation, exhaustion, extension or merge experiment.&lt;/p&gt;
&lt;p&gt;Before a merge, identify open users of both origin and snapshot and plan the application outage or activation boundary. Rehearse on disposable volumes and keep an independent backup. Size for all distinct changes across the retention window, not just its busiest hour. Confirm the actual resulting volume and restored application state; do not infer success solely from declining allocation counters.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;
&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-lvm-logical-volume-snapshot.detail.en.svg" alt="Classic LVM combines unchanged origin chunks with finite saved exceptions; overflow invalidation, thin-pool capacities and merge conditions are distinct." width="560" height="2262"&gt;
&lt;figcaption&gt;LVM keeps changed chunks — mechanism diagram.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;The unchanged lines below remain held. They contain environment-specific names and require authorized LVM access; &lt;code&gt;lvextend&lt;/code&gt; changes allocation, so this is not a read-only diagnostic block. No device-mapper, kernel, volume or mount operation was executed:&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;Classic snapshot Data% and its validity state differ from a thin pool&amp;rsquo;s Data% and Meta%. &lt;code&gt;dmsetup status&lt;/code&gt; output is target-specific: normal snapshot allocation need not fall, while snapshot-merge allocation can decrease toward its metadata remainder. The final line neither proves free VG capacity nor revives an invalid snapshot. A separately held complete exercise must bind targets and verify each claimed transition.&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 illustrative sizing commands, 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;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/lvconvert.8.html"&gt;lvconvert(8)&lt;/a&gt;, immediate versus deferred merge and open-volume conditions, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/18/backup-file.html"&gt;PostgreSQL filesystem backup&lt;/a&gt;, consistent snapshot scope, WAL and multi-volume recovery requirements, checked 2026-10-18.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>ZFS snapshots give the filesystem a rewind button</title><link>https://hk.xbcatm.com/posts/zfs-snapshots-multiversion-safety/</link><pubDate>Tue, 21 Mar 2028 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/posts/zfs-snapshots-multiversion-safety/</guid><description>A ZFS snapshot preserves a filesystem view within one pool; application consistency, retained space and destructive rollback need separate checks.</description><content:encoded>&lt;p&gt;A ZFS snapshot preserves a point-in-time dataset view through copy-on-write references instead of a full duplicate of its data. Existing blocks remain available while references need them. Creation still has metadata and synchronization work; neither zero allocation nor completion before a shell redraw is a measured guarantee. This review did not run ZFS or recover the earlier claimed fixture&amp;rsquo;s raw output.&lt;/p&gt;
&lt;p&gt;That makes retained history useful for local recovery, but every snapshot of &lt;code&gt;tank/data&lt;/code&gt; remains in the same pool. A failure that makes that pool unavailable can also make its snapshots unavailable. This is a shared failure boundary, not a claim that every controller fault atomically destroys all blocks. Independent backups or replication need their own retention and verified recovery.&lt;/p&gt;
&lt;h2 id="a-name-for-one-consistent-tree"&gt;A name for one consistent tree&lt;/h2&gt;
&lt;p&gt;Snapshots atomically capture the dataset&amp;rsquo;s filesystem state; recursive &lt;code&gt;-r&lt;/code&gt; snapshots include descendants at the same instant. Without recursion, child datasets are omitted rather than automatically captured later. Filesystem consistency is not an application checkpoint: database buffers, logs and files outside the selected datasets still require the application&amp;rsquo;s supported backup or crash-recovery procedure. Coordinate all required datasets before treating the set as recoverable.&lt;/p&gt;
&lt;p&gt;Names such as &lt;code&gt;tank/data@daily-2026-09-16&lt;/code&gt; can help retention, alongside creation times, properties and backup records. Naming alone does not prove eligibility for deletion. &lt;code&gt;zfs list -t snapshot -o name,used,refer,creation -s creation&lt;/code&gt; inspects snapshots and their accounting; dependencies and holds must also be checked before a pruning decision.&lt;/p&gt;
&lt;h2 id="where-the-space-is-in-zfs-list--o-space"&gt;Where the space is, in zfs list -o space&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;zfs list -o space tank/data&lt;/code&gt; selects NAME, AVAIL, USED, USEDSNAP, USEDDS, USEDREFRESERV and USEDCHILD; it does not include REFER. USED decomposes into the four used-by categories, not REFER plus USEDSNAP. REFER is all data accessible through the dataset and can include shared blocks. Snapshot USED measures uniquely referenced space; deleting another snapshot can change it, so individual values do not simply sum to all recoverable space.&lt;/p&gt;
&lt;p&gt;The earlier thirty-snapshot, 64 MiB dataset and 2 MiB daily-churn figures lack a recovered ZFS trace. The 60 MiB versus 1920 MiB comparison is illustrative arithmetic, not verified allocation. Metadata, compression, block size, sharing and the exact retention timeline affect actual cost. A recent snapshot may have little unique data while the whole retained set still prevents substantial reclamation.&lt;/p&gt;
&lt;h2 id="holds-diff-and-comparing-versions"&gt;Holds, diff, and comparing versions&lt;/h2&gt;
&lt;p&gt;A user hold prevents destruction until released. &lt;code&gt;zfs hold keep tank/data@daily-2026-09-16&lt;/code&gt; sets one; &lt;code&gt;zfs holds tank/data@daily-2026-09-16&lt;/code&gt; inspects it, and &lt;code&gt;zfs release keep tank/data@daily-2026-09-16&lt;/code&gt; releases that tag. Holds can protect an in-progress transfer, but clone-origin dependencies are separate and need not appear as named hold tags. Release only after understanding the dependent operation.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;zfs diff tank/data@daily-2026-09-15 tank/data@daily-2026-09-16&lt;/code&gt; reports changes between the retained versions: created, removed, modified and renamed paths. Filesystem snapshots are mounted on demand beneath &lt;code&gt;.zfs/snapshot/&amp;lt;name&amp;gt;&lt;/code&gt;; &lt;code&gt;snapdir=visible&lt;/code&gt; exposes that directory in listings. Snapshot access and a destructive dataset rollback are different recovery choices, and neither verifies application correctness by itself.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;A successful send/receive into independently protected storage can survive loss of the source pool. The shown incremental transfer requires an existing destination whose latest snapshot matches the source base; a bare incremental stream is not a complete standalone backup. A second pool on the same machine can still share power, controller or administrator risks. Verify received data and a usable restore before claiming protection.&lt;/p&gt;
&lt;p&gt;Growing USEDSNAP warrants capacity and retention review, but eighty-percent occupancy is not a universal write-failure threshold. Quotas, reservations and actual pool availability matter. A clone depends on its origin snapshot; deleting that origin or rolling back past newer dependent versions needs deliberate dependency handling. Rollback can discard newer data, and destroying clones is not the only possible recovery strategy.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;
&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-zfs-snapshots-multiversion-safety.detail.en.svg" alt="ZFS snapshot lineage and clone dependencies are separated from user holds and USED accounting; rollback and independent receive retain their limits." width="560" height="2205"&gt;
&lt;figcaption&gt;ZFS history has dependencies — mechanism diagram.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;This unexecuted standalone Bash recipe requires exclusively controlled disposable datasets, existing source snapshots, and a receiver previously seeded from the base. The destination must be unchanged since that base, mounted at the bound path without intervening mounts or symlinks; the hold tag must be unused. Before taking the target snapshot, prepare regular-file &lt;code&gt;probe.txt&lt;/code&gt; in the source and keep a trusted external copy at &lt;code&gt;expected&lt;/code&gt;. Listing is recursive; transfer covers only &lt;code&gt;tank/data&lt;/code&gt;, not descendants.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tank/data&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;backup/data&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;daily-2026-09-15&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;next&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;daily-2026-09-16&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;example-transfer&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mnt/zfs-lab/received&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mnt/zfs-lab/expected-v2.txt&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; zfs list -r -t snapshot -o name,used,refer,creation -s creation &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; zfs list -o name,avail,used,usedsnap,usedds,usedrefreserv,usedchild,refer &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; zfs holds &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; zfs diff &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;latest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs list -H -r -t snapshot -o name -s createtxg &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; awk -F@ -v &lt;span class="nv"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;$1 == dataset { last=$0 } END { print last }&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$latest&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;source_guid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs get -Hp -o value guid &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;target_guid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs get -Hp -o value guid &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$source_guid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$source_guid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$target_guid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;target_mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs get -H -o value mountpoint &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;target_mounted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs get -H -o value mounted &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$target_mount&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$mount&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$target_mounted&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; ! -L &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; zfs hold &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; zfs send -i &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; zfs receive &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;source_guid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs get -Hp -o value guid &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;target_guid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zfs get -Hp -o value guid &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$source_guid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$source_guid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$target_guid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$mount&lt;/span&gt;&lt;span class="s2"&gt;/.zfs/snapshot/&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;/probe.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; ! -L &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$mount&lt;/span&gt;&lt;span class="s2"&gt;/.zfs/snapshot/&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;/probe.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cmp -- &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$mount&lt;/span&gt;&lt;span class="s2"&gt;/.zfs/snapshot/&lt;/span&gt;&lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="s2"&gt;/probe.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The latest receiver snapshot and base GUID must match; equal names alone are insufficient. Failed checks stop the subshell, and &lt;code&gt;pipefail&lt;/code&gt; exposes either transfer failure. Received GUID and fixture-byte comparisons still do not establish a valid application restore: verify the complete manifest, metadata and application recovery separately. Any holds created remain after failure or success; after validation and dependency review, release only this exercise&amp;rsquo;s tag with &lt;code&gt;zfs release example-transfer tank/data@daily-2026-09-15 tank/data@daily-2026-09-16&lt;/code&gt;. No force rollback or automatic cleanup is included.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-snapshot.8.html"&gt;zfs-snapshot(8)&lt;/a&gt;, snapshot creation semantics and recursive snapshots, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-list.8.html"&gt;zfs-list(8)&lt;/a&gt;, the &lt;code&gt;space&lt;/code&gt; column set and separate REFER property, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-hold.8.html"&gt;zfs-hold(8)&lt;/a&gt;, holds, &lt;code&gt;zfs holds&lt;/code&gt;, and the EBUSY returned by destroy, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-send.8.html"&gt;zfs-send(8)&lt;/a&gt;, incremental send streams and their base-snapshot requirements, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/7/zfsprops.7.html"&gt;zfsprops(7)&lt;/a&gt;, used-by decomposition, shared references, quotas and reservations, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-clone.8.html"&gt;zfs-clone(8)&lt;/a&gt;, clone dependence on its origin snapshot, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-rollback.8.html"&gt;zfs-rollback(8)&lt;/a&gt;, discarded newer data and dependent clone handling, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-receive.8.html"&gt;zfs-receive(8)&lt;/a&gt;, matching destination base for incremental receive, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/18/backup-file.html"&gt;PostgreSQL filesystem backup&lt;/a&gt;, consistent snapshot scope, WAL and multi-volume recovery requirements, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/7/zfsconcepts.7.html"&gt;ZFS concepts&lt;/a&gt;, clone dependency on its origin snapshot and shared storage boundaries, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openzfs.github.io/openzfs-docs/man/master/8/zfs-get.8.html"&gt;zfs-get(8)&lt;/a&gt;, exact property reads used for GUID and mountpoint checks, checked 2026-10-19.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Btrfs snapshots are copy-on-write, not copies</title><link>https://hk.xbcatm.com/posts/btrfs-snapshots-are-copy-on-write/</link><pubDate>Sun, 14 Nov 2027 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/posts/btrfs-snapshots-are-copy-on-write/</guid><description>A Btrfs snapshot shares file extents within one filesystem; metadata, retained changes and application recovery still require planning.</description><content:encoded>&lt;p&gt;A Btrfs snapshot is a new subvolume with the source&amp;rsquo;s initial contents, sharing existing file extents rather than duplicating every data block. Creation copies root metadata and may flush dirty source data, so it is not guaranteed to finish in milliseconds. More than two subvolumes or reflinks can reference the same extent; there is no fixed two-reference limit.&lt;/p&gt;
&lt;p&gt;Snapshots make local recovery points economical, but their cost includes metadata and blocks retained by later divergence. They share the filesystem&amp;rsquo;s storage pool with the source, which may span several devices. Keeping many snapshots can retain deleted or overwritten data. A snapshot within that same filesystem is not an independent backup, even when it is read-only.&lt;/p&gt;
&lt;h2 id="shared-extents-not-copied-blocks"&gt;Shared extents, not copied blocks&lt;/h2&gt;
&lt;p&gt;Snapshots are subvolumes with their own roots. The example &lt;code&gt;btrfs subvolume snapshot -r &amp;quot;$src&amp;quot; &amp;quot;$snap&amp;quot;&lt;/code&gt; requests a read-only snapshot; ordinary snapshots are writable. Read-only status prevents routine changes through that subvolume, not administrator deletion or underlying corruption. Snapshotting is not recursive: nested subvolumes appear as empty stubs, so their contents require separate handling.&lt;/p&gt;
&lt;p&gt;When a shared extent changes, copy-on-write preserves the older contents for remaining references. Allocation depends on extent layout, compression, metadata and later snapshots, not simply application bytes written. The earlier APFS clone figures are not Btrfs measurements and have no recovered trace here. They cannot establish zero-cost creation or zero allocation on a repeated Btrfs overwrite.&lt;/p&gt;
&lt;h2 id="where-the-space-actually-goes"&gt;Where the space actually goes&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;btrfs filesystem du&lt;/code&gt; reports file-extent Total, Exclusive and Set shared values using FIEMAP. Each argument defines its own recursively scanned file set; Set shared counts shared extents referenced by that set, accounting for overlaps, rather than only sharing with other arguments. &lt;code&gt;compsize&lt;/code&gt; reports compression-oriented Disk Usage, Uncompressed and Referenced columns, not the same split. Qgroup accounting additionally requires enabled, consistent quotas.&lt;/p&gt;
&lt;p&gt;Deleting the source does not release extents still referenced by snapshots or reflinks. Those extents become exclusive only if no other references remain. Subvolume deletion removes the directory first and cleans extents asynchronously; space need not return immediately. &lt;code&gt;df&lt;/code&gt; gives filesystem-level capacity, not an attribution of every retained byte to one snapshot.&lt;/p&gt;
&lt;h2 id="a-restore-point-is-not-a-replica"&gt;A restore point is not a replica&lt;/h2&gt;
&lt;p&gt;A local snapshot can preserve a useful filesystem view without providing application-consistent recovery. A database may need coordinated backup procedures, its logs and recovery after restoration; unsent application buffers are outside the snapshot. Source and snapshot also share underlying failure risks. Local rollback is neither a fixed-time guarantee nor a substitute for testing that the application can restart correctly.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;btrfs send&lt;/code&gt; generates a stream for &lt;code&gt;btrfs receive&lt;/code&gt; on another filesystem. Incremental &lt;code&gt;-p&lt;/code&gt; operation relies on a suitable unchanged parent present at both ends; all participating snapshots must be read-only. Separate storage helps only against failures it does not share. Retention, successful receipt and content-verified restoration still matter; more frequent snapshots do not guarantee a fixed reduction in transferred bytes.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;When deleting a large directory frees little space, inspect snapshot and reflink references before deleting more data. &lt;code&gt;btrfs filesystem du -s&lt;/code&gt; and qgroups provide different accounting views; one Exclusive value need not explain the whole discrepancy. Only remove recovery points after checking retention and send dependencies. Several snapshots can collectively retain blocks that none owns exclusively by itself.&lt;/p&gt;
&lt;p&gt;Copy-on-write needs allocation headroom for data and metadata. Review both with &lt;code&gt;btrfs filesystem usage&lt;/code&gt;; unallocated device space and free space inside allocated block groups are different quantities. Neither a universal five-percent reserve nor a fixed order of exhaustion follows from these counters. An allocation failure alone also does not establish that the filesystem became read-only.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;
&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-btrfs-snapshots-are-copy-on-write.detail.en.svg" alt="Btrfs roots share extents while divergence preserves old data; nested-subvolume omissions, retained references and independent recovery requirements remain explicit." width="560" height="2117"&gt;
&lt;figcaption&gt;Btrfs shared extents — mechanism diagram.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;This limited inspection remains unexecuted. It requires authorized disposable source &lt;code&gt;/mnt/btrfs-lab/data&lt;/code&gt;, an existing &lt;code&gt;snapshots&lt;/code&gt; parent on the same Btrfs filesystem, tools and permissions. Keep all paths symlink-free and exclusively controlled throughout. An existing destination directory changes where Btrfs creates the snapshot; the guard rejects existing paths, including dangling symlinks. It is not a race-proof lock; stop if exclusive control is unavailable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mnt/btrfs-lab/data&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;snap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mnt/btrfs-lab/snapshots/data-2026-09-16&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$snap&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -L &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$snap&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Refusing an existing snapshot destination.&amp;#39;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; btrfs subvolume snapshot -r &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$snap&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; btrfs filesystem du -s &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$snap&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; compsize &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$snap&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; btrfs filesystem usage &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Snapshot inspection now uses the bound destination; reruns refuse it rather than removing it. File-extent counters do not measure total snapshot metadata. Exclusive equalling Total means currently unshared extents, not absence of snapshot history. This is not a divergence or restore test: separately record known fixture bytes, verify both views initially, change only the source, then verify old snapshot bytes and new source bytes. Those storage checks and application recovery remain held.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://btrfs.readthedocs.io/en/latest/btrfs-subvolume.html"&gt;btrfs subvolume documentation&lt;/a&gt;, subvolume and snapshot semantics including read-only snapshots, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://btrfs.readthedocs.io/en/latest/btrfs-filesystem.html"&gt;btrfs filesystem documentation&lt;/a&gt;, defines the &lt;code&gt;du&lt;/code&gt; Exclusive and Set shared columns used above, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://btrfs.readthedocs.io/en/latest/btrfs-qgroup.html"&gt;btrfs qgroup documentation&lt;/a&gt;, exclusive and referenced byte accounting per qgroup, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://btrfs.readthedocs.io/en/latest/btrfs-send.html"&gt;btrfs send documentation&lt;/a&gt;, send-stream generation and read-only incremental-base requirements, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kilobyte/compsize/blob/master/compsize.8"&gt;compsize upstream manual&lt;/a&gt;, compression and referenced-size accounting differs from filesystem du, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/18/backup-file.html"&gt;PostgreSQL filesystem backup&lt;/a&gt;, consistent snapshot scope, WAL and multi-volume recovery requirements, checked 2026-10-18.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>