<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fh="http://purl.org/syndication/history/1.0"><channel><title>SysopKit | Blog</title><description>TypeScript infrastructure automation toolkit.</description><link>https://www.sysopkit.com/</link><language>en</language><fh:complete/><atom:link rel="self" href="https://www.sysopkit.com/blog/rss.xml"/><item><title>Ephemeral Chrome Profiles in tmpfs</title><link>https://www.sysopkit.com/blog/ephemeral-chrome-profiles-in-tmpfs/</link><guid isPermaLink="true">https://www.sysopkit.com/blog/ephemeral-chrome-profiles-in-tmpfs/</guid><description>Throwaway Chrome sessions with full logins and extensions — clone a real profile into tmpfs, route links to the live session, and delete everything on exit.</description><pubDate>Mon, 28 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Chrome is write-heavy: shader and GPU caches, media cache, history, and session state churn on every page load. On an SSD-backed home directory that means constant small writes for data with no long-term value. Incognito mode avoids the writes but also drops logins and extensions, and a second persistent profile accumulates the same pollution as the main one.&lt;/p&gt;
&lt;p&gt;The setup below takes a different approach: clone a real profile into &lt;code dir=&quot;auto&quot;&gt;tmpfs&lt;/code&gt; (&lt;code dir=&quot;auto&quot;&gt;/tmp&lt;/code&gt;), run Chrome there with an isolated &lt;code dir=&quot;auto&quot;&gt;--user-data-dir&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;--disk-cache-dir&lt;/code&gt;, and delete the copy when the window closes. Each session starts with working logins and extensions, no browsing history is written back to the SSD, and cache I/O never touches the disk.&lt;/p&gt;
&lt;p&gt;The full script, for reference:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eEuo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipefail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;CHROME_CONFIG_DIR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/.config/google-chrome&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;$#&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-gt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;PROFILE_NAME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;PROFILE_NAME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SOURCE_PROFILE&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CHROME_CONFIG_DIR&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$PROFILE_NAME&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SOURCE_PROFILE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Error: Profile &apos;&lt;/span&gt;&lt;span&gt;$PROFILE_NAME&lt;/span&gt;&lt;span&gt;&apos; not found at &lt;/span&gt;&lt;span&gt;$SOURCE_PROFILE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/google-chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;TMP_ROOT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;mktemp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/google-chrome&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;profile.XXXXXX&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;TMP_USER_DATA&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_ROOT&lt;/span&gt;&lt;span&gt;/user-data&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;TMP_DISK_CACHE&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_ROOT&lt;/span&gt;&lt;span&gt;/disk-cache&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;CURRENT_LINK&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/tmp/google-chrome/current&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# First running profile wins: don&apos;t steal the link from a live session.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sfn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_USER_DATA&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_ROOT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;readlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_USER_DATA&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;INT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TERM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_DISK_CACHE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_USER_DATA&lt;/span&gt;&lt;span&gt;/Default&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SOURCE_PROFILE&lt;/span&gt;&lt;span&gt;/.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_USER_DATA&lt;/span&gt;&lt;span&gt;/Default/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;google-chrome&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--disk-cache-dir=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_DISK_CACHE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user-data-dir=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_USER_DATA&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-first-run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$@&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;why-a-snapshot-clone&quot;&gt;Why a snapshot clone&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Three options exist for a throwaway session, and only the clone preserves convenience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Incognito&lt;/strong&gt; — clean writes, but no logins, no extensions. Every session starts from zero.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Second persistent profile&lt;/strong&gt; — keeps logins, but history, cookies, and cache accumulate permanently and still live on the SSD.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Snapshot clone&lt;/strong&gt; — copies the source profile once at launch. The session starts already logged in, with extensions enabled; all writes land in &lt;code dir=&quot;auto&quot;&gt;tmpfs&lt;/code&gt;, and nothing is merged back. Closing the window deletes the copy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Logged-in state is the major advantage over Incognito. The source profile acts as a credentials store: it is opened directly only for maintenance, while everyday browsing happens in disposable clones that inherit its cookies.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;keeping-source-profiles-clean-and-lightweight&quot;&gt;Keeping source profiles clean and lightweight&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Because the whole profile is copied at every launch, the source profile should be kept small. The recommended setup is a dedicated profile per purpose (for example, one for development services, one for media): create the profile, log in to the services it needs, then clear the disk cache while keeping cookies. Clearing cache via &lt;code dir=&quot;auto&quot;&gt;Delete browsing data&lt;/code&gt; with only &lt;code dir=&quot;auto&quot;&gt;Cached images and files&lt;/code&gt; selected (time range &lt;code dir=&quot;auto&quot;&gt;All time&lt;/code&gt;) removes the bulky regenerable data without touching logins.&lt;/p&gt;
&lt;p&gt;Session cookies expire. Some services, such as GitHub, require cookie revalidation roughly once a week. The refresh workflow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Launch Chrome once with the source profile directly (not the tmp script).&lt;/li&gt;
&lt;li&gt;Visit the service — the session typically refreshes without showing a login screen.&lt;/li&gt;
&lt;li&gt;Clear cached images and files again, keeping cookies.&lt;/li&gt;
&lt;li&gt;Close the window. Subsequent tmp clones inherit the refreshed cookies.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps each launch fast (small copy), each session authenticated, and the SSD free of per-session churn.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-tmpfs&quot;&gt;Why tmpfs&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;/tmp&lt;/code&gt; is typically &lt;code dir=&quot;auto&quot;&gt;tmpfs&lt;/code&gt;, meaning files exist only in memory and swap:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No SSD wear&lt;/strong&gt; for cache, history, and session churn during the session.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt; cache I/O without disk latency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-cleaning&lt;/strong&gt; — a reboot empties &lt;code dir=&quot;auto&quot;&gt;/tmp&lt;/code&gt; even if a session is killed uncleanly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each launch creates a fresh &lt;code dir=&quot;auto&quot;&gt;profile.XXXXXX&lt;/code&gt; directory with separate &lt;code dir=&quot;auto&quot;&gt;user-data&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;disk-cache&lt;/code&gt; subdirectories. Pointing &lt;code dir=&quot;auto&quot;&gt;--disk-cache-dir&lt;/code&gt; outside the user-data tree keeps evictable cache I/O isolated from profile state.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;script-walkthrough&quot;&gt;Script walkthrough&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;$#&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-gt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;PROFILE_NAME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;PROFILE_NAME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The first argument selects the source profile; everything after it is forwarded to Chrome via &lt;code dir=&quot;auto&quot;&gt;&quot;$@&quot;&lt;/code&gt; at the end. That forwarding is what lets desktop actions and terminal invocations pass URLs into the fresh session.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/google-chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;TMP_ROOT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;mktemp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/google-chrome&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;profile.XXXXXX&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;mktemp -d&lt;/code&gt; creates a uniquely named session directory with &lt;code dir=&quot;auto&quot;&gt;0700&lt;/code&gt; permissions, which matters because the copy contains cookies and session tokens. The &lt;code dir=&quot;auto&quot;&gt;mkdir -p&lt;/code&gt; guards against a fresh boot where &lt;code dir=&quot;auto&quot;&gt;/tmp/google-chrome&lt;/code&gt; does not exist yet.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;CURRENT_LINK&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/tmp/google-chrome/current&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sfn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_USER_DATA&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT_LINK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;/tmp/google-chrome/current&lt;/code&gt; points at the live session’s &lt;code dir=&quot;auto&quot;&gt;user-data&lt;/code&gt; directory so external link openers have a stable path. The first running profile wins: a second concurrent session keeps its own directory but does not steal the link. Its cleanup only removes the symlink when the link still points at its own directory (checked with &lt;code dir=&quot;auto&quot;&gt;readlink&lt;/code&gt;), so closing the second session never disconnects the first.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_ROOT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;INT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TERM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Exiting Chrome triggers removal of the whole session directory — profile copy, cache, and all browsing traces. There is no merge-back step by design.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;persistent-base-directory-with-user-tmpfiles&quot;&gt;Persistent base directory with user tmpfiles&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;mkdir -p&lt;/code&gt; in the script is enough for correctness, but declaring the base directory via systemd user tmpfiles makes the intent explicit and guarantees correct ownership and mode at login:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/.config/user-tmpfiles.d/google-chrome.conf&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/google-chrome 0700 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Apply with &lt;code dir=&quot;auto&quot;&gt;systemd-tmpfiles --user --create&lt;/code&gt; (or log out and back in). The script’s &lt;code dir=&quot;auto&quot;&gt;mkdir -p&lt;/code&gt; remains as a fallback for systems without the entry.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;desktop-integration&quot;&gt;Desktop integration&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Two user-local desktop entries wire the script into the environment. User entries live in &lt;code dir=&quot;auto&quot;&gt;~/.local/share/applications/&lt;/code&gt; and override the system copies in &lt;code dir=&quot;auto&quot;&gt;/usr/share/applications/&lt;/code&gt;, so copy the file there first and edit the copy — package upgrades can overwrite the system one:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/share/applications/google-chrome.desktop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.local/share/applications/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The script itself can live anywhere on &lt;code dir=&quot;auto&quot;&gt;PATH&lt;/code&gt;, for example &lt;code dir=&quot;auto&quot;&gt;~/.local/bin/google-chrome-tmp-profile&lt;/code&gt; (note that &lt;code dir=&quot;auto&quot;&gt;Exec=&lt;/code&gt; does not expand &lt;code dir=&quot;auto&quot;&gt;~&lt;/code&gt;, so the desktop file must contain the absolute path, e.g. &lt;code dir=&quot;auto&quot;&gt;/home/user/.local/bin/google-chrome-tmp-profile&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;First, add a launcher action that starts a tmp session, optionally with a URL. Register the action in the main group, then define its own group:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# in ~/.local/share/applications/google-chrome.desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Actions&lt;/span&gt;&lt;span&gt;=new-window&lt;/span&gt;&lt;span&gt;;new-private-window;new-tmp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Desktop Action new-tmp]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;=Temp Profile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Exec&lt;/span&gt;&lt;span&gt;=/home/user/.local/bin/google-chrome-tmp-profile Default %U&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;%U&lt;/code&gt; passes clicked URLs or files as extra arguments, which the script forwards to Chrome via &lt;code dir=&quot;auto&quot;&gt;&quot;$@&quot;&lt;/code&gt;. Repeat the pattern for more source profiles (e.g. a &lt;code dir=&quot;auto&quot;&gt;new-tmp-work&lt;/code&gt; action pointing at a &lt;code dir=&quot;auto&quot;&gt;Work&lt;/code&gt; profile). After editing, right-clicking the Chrome icon offers &lt;code dir=&quot;auto&quot;&gt;Temp Profile&lt;/code&gt; alongside the standard window actions.&lt;/p&gt;
&lt;p&gt;Second, add a hidden default-browser shim that routes external links (from a terminal, chat app, or &lt;code dir=&quot;auto&quot;&gt;xdg-open&lt;/code&gt;) into the live tmp session. Save it as a separate file, e.g. &lt;code dir=&quot;auto&quot;&gt;~/.local/share/applications/google-chrome-tmp-current.desktop&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Desktop Entry]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt;=Application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Version&lt;/span&gt;&lt;span&gt;=1.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;=Google Chrome (current tmp profile)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;NoDisplay&lt;/span&gt;&lt;span&gt;=true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Terminal&lt;/span&gt;&lt;span&gt;=false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Icon&lt;/span&gt;&lt;span&gt;=google-chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;MimeType&lt;/span&gt;&lt;span&gt;=x-scheme-handler/http&lt;/span&gt;&lt;span&gt;;x-scheme-handler/https;text/html;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Exec&lt;/span&gt;&lt;span&gt;=/usr/bin/bash -c &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;p=&quot;/tmp/google-chrome/current&quot;; [[ -L &quot;$p&quot; &amp;#x26;&amp;#x26; -d &quot;$p&quot; ]] &amp;#x26;&amp;#x26; exec &quot;/usr/bin/google-chrome-stable&quot; --user-data-dir=&quot;$p&quot; &quot;$@&quot; || exec /usr/bin/true&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; bash %U&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then hand URL ownership to the shim:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Comment out the &lt;code dir=&quot;auto&quot;&gt;MimeType=&lt;/code&gt; line in &lt;code dir=&quot;auto&quot;&gt;~/.local/share/applications/google-chrome.desktop&lt;/code&gt; so the regular entry no longer claims URLs.&lt;/li&gt;
&lt;li&gt;Run &lt;code dir=&quot;auto&quot;&gt;update-desktop-database ~/.local/share/applications&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the shim as the default browser (desktop settings → Default Applications, or &lt;code dir=&quot;auto&quot;&gt;xdg-settings set default-web-browser google-chrome-tmp-current.desktop&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Two details matter in the shim. The &lt;code dir=&quot;auto&quot;&gt;bash -c &apos;...&apos; bash %U&lt;/code&gt; form with &lt;code dir=&quot;auto&quot;&gt;&quot;$@&quot;&lt;/code&gt; forwards &lt;strong&gt;all&lt;/strong&gt; URLs; using &lt;code dir=&quot;auto&quot;&gt;&quot;$0&quot;&lt;/code&gt; would deliver only the first one and drop the rest. And when no tmp session is running, the shim intentionally does nothing (&lt;code dir=&quot;auto&quot;&gt;/usr/bin/true&lt;/code&gt;): there is no reliable way to surface a notification from a background URL open, so silently dropping is preferable to spawning an unwanted persistent-profile window.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;behavior-summary&quot;&gt;Behavior summary&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Launch a tmp session from the application menu; it opens with existing logins and extensions.&lt;/li&gt;
&lt;li&gt;Links clicked anywhere else open as new tabs in the running tmp session via the &lt;code dir=&quot;auto&quot;&gt;current&lt;/code&gt; symlink.&lt;/li&gt;
&lt;li&gt;With no tmp session running, external links are dropped instead of leaking into a persistent profile.&lt;/li&gt;
&lt;li&gt;A second concurrent tmp session runs isolated but does not hijack link routing.&lt;/li&gt;
&lt;li&gt;Closing the session window deletes its &lt;code dir=&quot;auto&quot;&gt;tmpfs&lt;/code&gt; copy; a reboot clears anything left behind.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>chrome</category><category>tmpfs</category><category>ssd</category><category>linux</category><category>bash</category><category>desktop</category></item><item><title>Clearing Orphaned Bun Cache Entries</title><link>https://www.sysopkit.com/blog/bun-clear-cache/</link><guid isPermaLink="true">https://www.sysopkit.com/blog/bun-clear-cache/</guid><description>Surgical cleanup of the Bun install cache — delete extracted package directories with no remaining hard links instead of wiping the whole cache.</description><pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The Bun install cache accumulates every downloaded package version. By default it lives at &lt;code dir=&quot;auto&quot;&gt;~/.bun/install/cache&lt;/code&gt; (or &lt;code dir=&quot;auto&quot;&gt;$BUN_INSTALL/install/cache&lt;/code&gt; when &lt;code dir=&quot;auto&quot;&gt;BUN_INSTALL&lt;/code&gt; is set). Over time it grows into gigabytes of stale data from upgraded or removed dependencies. The blunt option, &lt;code dir=&quot;auto&quot;&gt;bun pm cache rm&lt;/code&gt;, deletes everything and forces a full re-download on the next install. The script below takes a surgical approach: it removes only extracted package directories that are no longer referenced by any &lt;code dir=&quot;auto&quot;&gt;node_modules&lt;/code&gt; tree.&lt;/p&gt;
&lt;p&gt;The full script, for reference:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-euo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipefail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;shopt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nullglob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;BUN_INSTALL&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bun&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Error: Bun cache directory not found: &lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;patterns&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;/*@@@*/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;/@*/*@@@*/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pattern&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;patterns&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# shellcheck disable=SC2086 -- intentional glob expansion&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$pattern&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;# Count files with active hard links&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;linked_files&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-l&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;# If no files are linked elsewhere, remove the target&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$linked_files&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Removing: &lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mindepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-xtype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;l&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mindepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;bun-cache-layout&quot;&gt;Bun cache layout&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The cache directory contains three kinds of entries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;hash&gt;.npm&lt;/code&gt; files — registry manifest metadata (&lt;code dir=&quot;auto&quot;&gt;bun-npm-manifest-cache&lt;/code&gt; format: version lists and tarball URLs), keyed by content hash (for example, &lt;code dir=&quot;auto&quot;&gt;009c472d14d8a98f.npm&lt;/code&gt;). These are not package archives; no local tarballs are stored in the cache.&lt;/li&gt;
&lt;li&gt;Extracted directories — unpacked packages named &lt;code dir=&quot;auto&quot;&gt;name@version@@@1&lt;/code&gt; (for example, &lt;code dir=&quot;auto&quot;&gt;acorn@8.18.0@@@1&lt;/code&gt;). Scoped packages nest one level deeper: &lt;code dir=&quot;auto&quot;&gt;@scope/name@version@@@1&lt;/code&gt; (for example, &lt;code dir=&quot;auto&quot;&gt;@openai/codex@0.153.4@@@1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Alias symlinks — version aliases such as &lt;code dir=&quot;auto&quot;&gt;oxlint/1.83.0@@@1 -&gt; ../oxlint@1.83.0@@@1&lt;/code&gt; that Bun uses to resolve version ranges.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two glob patterns at the top of the script cover both extracted-directory shapes: &lt;code dir=&quot;auto&quot;&gt;$BUN_CACHE_DIR/*@@@*/&lt;/code&gt; for unscoped packages and &lt;code dir=&quot;auto&quot;&gt;$BUN_CACHE_DIR/@*/*@@@*/&lt;/code&gt; for scoped ones. The &lt;code dir=&quot;auto&quot;&gt;.npm&lt;/code&gt; manifest files match neither pattern and are left untouched, which is deliberate: all 728 of them total around 36 MB out of a 2.7 GB cache (~1.3%), and they are needed for dependency resolution. Their hash-based filenames carry no package mapping and they are never hard-linked, so the link-count check below cannot apply to them.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;detection-hard-link-counts&quot;&gt;Detection: hard-link counts&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;When Bun installs dependencies, it hard-links cache files into the project’s &lt;code dir=&quot;auto&quot;&gt;node_modules&lt;/code&gt; instead of copying them. A cache file referenced by one project therefore has a link count of 2 — one link in the cache, one in &lt;code dir=&quot;auto&quot;&gt;node_modules&lt;/code&gt;. This is verifiable with &lt;code dir=&quot;auto&quot;&gt;stat&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$ stat -c &apos;%h %i %n&apos; ~/.bun/install/cache/acorn@8.18.0@@@1/package.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;2 13703410 /home/user/.bun/install/cache/acorn@8.18.0@@@1/package.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$ stat -c &apos;%h %i %n&apos; docs/node_modules/acorn/package.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;2 13703410 /home/user/docs/node_modules/acorn/package.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Same inode (&lt;code dir=&quot;auto&quot;&gt;13703410&lt;/code&gt;), link count 2 — the two paths are the same data on disk. When every project using that version is upgraded or removed, the &lt;code dir=&quot;auto&quot;&gt;node_modules&lt;/code&gt; links disappear and the count drops back to 1.&lt;/p&gt;
&lt;p&gt;That property is the entire detection mechanism:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;linked_files&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-l&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$linked_files&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Removing: &lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;find -links +1&lt;/code&gt; lists files with more than one hard link. A count of zero means no file in that extracted directory is referenced from anywhere else, so the directory is an orphan and safe to delete. Any directory with at least one linked file is kept. Because the check is per-file rather than per-directory, a partially shared directory is never removed.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;cleanup-dangling-symlinks-and-empty-directories&quot;&gt;Cleanup: dangling symlinks and empty directories&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Deleting an extracted directory orphans the alias symlinks pointing at it. The first &lt;code dir=&quot;auto&quot;&gt;find&lt;/code&gt; pass removes them:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-xtype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;l&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;-xtype l&lt;/code&gt; matches symlinks whose target no longer exists (broken links), as opposed to &lt;code dir=&quot;auto&quot;&gt;-type l&lt;/code&gt;, which matches all symlinks. Only dangling aliases are deleted; healthy aliases pointing at still-used versions are preserved. &lt;code dir=&quot;auto&quot;&gt;-mindepth 1&lt;/code&gt; keeps the cache root itself out of range on both &lt;code dir=&quot;auto&quot;&gt;find&lt;/code&gt; passes, so the root directory can never be deleted even if it were somehow empty.&lt;/p&gt;
&lt;p&gt;Removing directories and symlinks can leave empty parent directories behind — for example, a scope directory such as &lt;code dir=&quot;auto&quot;&gt;@openai/codex/&lt;/code&gt; after its last version is removed. The second pass clears those:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;what-the-script-leaves-alone&quot;&gt;What the script leaves alone&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;.npm&lt;/code&gt; manifest files are never evaluated and remain in the cache. Since the cache holds no local tarballs, reinstalling a deleted version re-downloads the archive from the registry — that network cost is why the link-count check is conservative: a directory is removed only when zero files in it are referenced from anywhere else, and anything ambiguous (for example, filenames with newlines that inflate the &lt;code dir=&quot;auto&quot;&gt;wc -l&lt;/code&gt; count) fails closed toward keeping the directory.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;safety-notes-and-limits&quot;&gt;Safety notes and limits&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The check is idempotent. Re-running the script after a cleanup finds zero candidates and changes nothing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;BUN_CACHE_DIR&lt;/code&gt; resolves from the environment with fallbacks: an explicitly exported &lt;code dir=&quot;auto&quot;&gt;BUN_CACHE_DIR&lt;/code&gt; wins, otherwise &lt;code dir=&quot;auto&quot;&gt;${BUN_INSTALL}/install/cache&lt;/code&gt; is used when &lt;code dir=&quot;auto&quot;&gt;BUN_INSTALL&lt;/code&gt; is set, otherwise the Bun default &lt;code dir=&quot;auto&quot;&gt;~/.bun/install/cache&lt;/code&gt;. The script aborts with an error if the resolved directory does not exist, instead of running &lt;code dir=&quot;auto&quot;&gt;find&lt;/code&gt; against a bad path.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;shopt -s nullglob&lt;/code&gt; makes an unmatched glob expand to nothing, so a cache with no entries of one shape is handled cleanly. The &lt;code dir=&quot;auto&quot;&gt;[ -d &quot;$dir&quot; ]&lt;/code&gt; guard remains as a second layer. Filenames cannot contain spaces in this cache layout (package names plus &lt;code dir=&quot;auto&quot;&gt;@@@&lt;/code&gt; separators), so the intentional unquoted glob expansion is safe here.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;rm -rf --&lt;/code&gt; uses an end-of-options marker so a pathological directory name starting with &lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt; can never be parsed as a flag.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The script must not run concurrently with &lt;code dir=&quot;auto&quot;&gt;bun install&lt;/code&gt;. A package mid-extraction has a link count of 1 and would look like an orphan.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deletion is irreversible outside of re-download. A dry run that lists candidates without deleting:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;BUN_INSTALL&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bun&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BUN_CACHE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/*@@@*/&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$d&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-l&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ] &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$d&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.local/bin/bun-clear-cache&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bun-clear-cache&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The cache location is picked up from &lt;code dir=&quot;auto&quot;&gt;BUN_INSTALL&lt;/code&gt; automatically; override it per-run with &lt;code dir=&quot;auto&quot;&gt;BUN_CACHE_DIR=/path/to/cache bun-clear-cache&lt;/code&gt; when needed.&lt;/p&gt;
&lt;p&gt;Running it after dependency upgrades or removals keeps the cache proportional to the actually installed dependency set.&lt;/p&gt;
</content:encoded><category>bun</category><category>javascript</category><category>disk-usage</category><category>tips</category><category>bash</category></item><item><title>Reducing SSD Wear with systemd-tmpfiles and tmpfs</title><link>https://www.sysopkit.com/blog/reduce-ssd-wear-with-tmpfiles/</link><guid isPermaLink="true">https://www.sysopkit.com/blog/reduce-ssd-wear-with-tmpfiles/</guid><description>Keep build outputs, caches, logs, and temp files in tmpfs instead of on the SSD.</description><pubDate>Wed, 16 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most of what lands on a disk every day does not need to survive a reboot: build artifacts, package caches, browser caches, logs, temp files. Moving those to &lt;code dir=&quot;auto&quot;&gt;tmpfs&lt;/code&gt; (&lt;code dir=&quot;auto&quot;&gt;/tmp&lt;/code&gt;) keeps them in memory instead of wearing out the SSD, and directories declared via user &lt;code dir=&quot;auto&quot;&gt;tmpfiles.d&lt;/code&gt; come back automatically at login — a reboot cleans up whatever is left.&lt;/p&gt;
&lt;p&gt;User entries live in &lt;code dir=&quot;auto&quot;&gt;~/.config/user-tmpfiles.d/*.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/build/cargo 0755 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;D /tmp/logs/app 0755 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Apply without logging out:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;systemd-tmpfiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;d&lt;/code&gt; creates the directory if missing; &lt;code dir=&quot;auto&quot;&gt;D&lt;/code&gt; additionally empties it on boot — suitable for build trees, unsuitable for anything with cross-boot value. See the &lt;a href=&quot;https://www.sysopkit.com/linux/systemd/tmpfiles/&quot;&gt;tmpfiles reference&lt;/a&gt; for the full type list.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;build-outputs&quot;&gt;Build outputs&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Compilers rewrite the same files on every invocation. Point the output directory at tmpfs:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CARGO_TARGET_DIR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;tmp&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;cargo&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;myproject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cargo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Use one subdirectory per project to avoid lock contention. The same pattern works for &lt;code dir=&quot;auto&quot;&gt;cmake -B /tmp/build/&amp;#x3C;project&gt;&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;GOCACHE=/tmp/build/go-cache&lt;/code&gt;, Jest &lt;code dir=&quot;auto&quot;&gt;--cacheDirectory&lt;/code&gt;, or &lt;code dir=&quot;auto&quot;&gt;.pytest_cache&lt;/code&gt; — the next build recreates everything, so losing the directory on reboot only costs a rebuild.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;package-manager-temp-and-logs&quot;&gt;Package manager temp and logs&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Per-operation temp and log directories are never reused across boots, so they are free to move:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/pkg/npm-logs 0755 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/pkg/prebuilds 0755 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm_config_cache&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;tmp&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;pkg&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;npm-cache&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Moving the full download cache saves writes but costs re-downloads after reboot — worth it on fast networks, otherwise move only the logs and temp extraction dirs.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;browser-and-app-caches&quot;&gt;Browser and app caches&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Browsers update disk cache, shader and GPU cache on nearly every page load. Declare the target and symlink or point the app’s cache setting at it:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/cache/chromium 0700 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/cache/thumbnails 0755 - - -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/cache/thumbnails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.cache/thumbnails&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Redirect only cache subdirectories (&lt;code dir=&quot;auto&quot;&gt;Cache&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;GPUCache&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;ShaderCache&lt;/code&gt;), never the whole profile — cookies and settings stay on disk. Use &lt;code dir=&quot;auto&quot;&gt;0700&lt;/code&gt; for paths that may hold session tokens, since &lt;code dir=&quot;auto&quot;&gt;/tmp&lt;/code&gt; is world-readable.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;logs-and-crash-dumps&quot;&gt;Logs and crash dumps&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Append-heavy logs that are almost never read are safe to redirect wholesale, with age-based cleanup bounding tmpfs usage:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;d /tmp/logs/app 0755 - - 7d -&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The tradeoff for all of the above: tmpfs shares RAM with applications and is empty after reboot, so keep working sets comfortably below free RAM and expect a slower first build or page load after boot.&lt;/p&gt;
</content:encoded><category>systemd</category><category>tmpfiles</category><category>tmpfs</category><category>ssd</category><category>linux</category></item><item><title>Lightweight Home Backup with Btrfs Snapshots and Rsync</title><link>https://www.sysopkit.com/blog/lightweight-home-backup/</link><guid isPermaLink="true">https://www.sysopkit.com/blog/lightweight-home-backup/</guid><description>A dependency-free Bash script for crash-consistent /home backups — Btrfs read-only snapshot, rsync with --link-dest, per-directory .rsync-filter excludes, Btrfs scrub, and time-based retention.</description><pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post describes a lightweight script that backs up &lt;code dir=&quot;auto&quot;&gt;/home&lt;/code&gt; to an external vault drive with minimal moving parts. No daemon, no database, no proprietary format — just a Btrfs snapshot plus &lt;code dir=&quot;auto&quot;&gt;rsync&lt;/code&gt;. The result on the backup drive is a plain directory tree that can be browsed, diffed, or synced back with &lt;code dir=&quot;auto&quot;&gt;rsync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The full script, for reference:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eEuo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipefail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$EUID&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ne&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$0&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$@&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;VAULT_DRIVES&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/mnt/vault/a&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/mnt/vault/b&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BTRFS_SRC_SUBVOL&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/home&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BTRFS_SNAPSHOTS_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/.snapshots&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RETENTION_DAYS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DATETIME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+%Y-%m-%d_%H-%M&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;VAULT_DRIVES&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;}/.mounted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$path&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Error: No valid backup destination mounted.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BTRFS_SNAPSHOTS_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BTRFS_SNAPSHOTS_PATH&lt;/span&gt;&lt;span&gt;/tmp-backup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subvolume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Cleaning up temporary snapshot: &lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subvolume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Warning: Failed to delete &lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXIT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Creating local read-only snapshot...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subvolume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;snapshot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BTRFS_SRC_SUBVOL&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BACKUP_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/&lt;/span&gt;&lt;span&gt;$DATETIME&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/latest&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RSYNC_OPTS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;-aAXHS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--numeric-ids&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--filter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: .rsync-filter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--info&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;progress2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-e&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;RSYNC_OPTS&lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--link-dest&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;elif&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Warning: ignoring broken &apos;latest&apos; symlink for --link-dest.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Starting backup to: &lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rsync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;RSYNC_OPTS&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;touch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sfn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ $(&lt;/span&gt;&lt;span&gt;findmnt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-no&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FSTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-T&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Starting btrfs scrub on &lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scrub&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-B&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-q&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Scrub complete: No corruption detected on the backup drive.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;CRITICAL: Btrfs scrub detected errors on &lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Check &apos;dmesg&apos; or &apos;btrfs scrub status &lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&apos; for details.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Cleaning up backups older than &lt;/span&gt;&lt;span&gt;$RETENTION_DAYS&lt;/span&gt;&lt;span&gt; days...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LATEST_TARGET&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;LATEST_TARGET&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;readlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$LATEST_TARGET&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mindepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-maxdepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mtime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$RETENTION_DAYS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$LATEST_TARGET&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mindepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-maxdepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mtime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$RETENTION_DAYS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Backup finished&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;TIP: If backup drive is getting slow, run &apos;btrfs balance&apos;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;backup-destination-selection&quot;&gt;Backup destination selection&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;VAULT_DRIVES&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/mnt/vault/a&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/mnt/vault/b&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The script supports multiple vault mount points and picks the first one that contains &lt;code dir=&quot;auto&quot;&gt;.mounted&lt;/code&gt; sentinel file:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;VAULT_DRIVES&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;}/.mounted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$path&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Error: No valid backup destination mounted.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;.mounted&lt;/code&gt; sentinel file is the key detail. Checking only whether &lt;code dir=&quot;auto&quot;&gt;/mnt/vault/a&lt;/code&gt; exists is not enough — an empty mount point directory exists even when the drive is unplugged, and a backup would then silently fill the root filesystem. Requiring a marker file that only exists on the mounted filesystem itself makes “no drive plugged in” a hard error instead of a silent disaster.&lt;/p&gt;
&lt;p&gt;Each run creates a timestamped directory under the chosen drive:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DATETIME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+%Y-%m-%d_%H-%M&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BACKUP_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/&lt;/span&gt;&lt;span&gt;$DATETIME&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/latest&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;So the layout becomes &lt;code dir=&quot;auto&quot;&gt;backup/2026-09-14_10-30/&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;backup/2026-09-15_10-30/&lt;/code&gt;, plus a &lt;code dir=&quot;auto&quot;&gt;backup/latest&lt;/code&gt; symlink pointing at the most recent one.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;crash-consistent-source-read-only-btrfs-snapshot&quot;&gt;Crash-consistent source: read-only Btrfs snapshot&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Backing up a live &lt;code dir=&quot;auto&quot;&gt;/home&lt;/code&gt; directly with &lt;code dir=&quot;auto&quot;&gt;rsync&lt;/code&gt; races with running applications — files changing mid-transfer produce an inconsistent copy. The script freezes a point-in-time view first:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BTRFS_SRC_SUBVOL&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/home&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;BTRFS_SNAPSHOTS_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/.snapshots&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BTRFS_SNAPSHOTS_PATH&lt;/span&gt;&lt;span&gt;/tmp-backup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BTRFS_SNAPSHOTS_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subvolume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Cleaning up temporary snapshot: &lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subvolume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Warning: Failed to delete &lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXIT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subvolume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;snapshot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BTRFS_SRC_SUBVOL&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;-r&lt;/code&gt; creates a read-only snapshot, which is both a safety measure (nothing during the backup can mutate the source view) and nearly free on Btrfs — snapshot creation is instant regardless of &lt;code dir=&quot;auto&quot;&gt;/home&lt;/code&gt; size because blocks are shared until they diverge.&lt;/p&gt;
&lt;p&gt;The snapshot is temporary by design. A &lt;code dir=&quot;auto&quot;&gt;trap&lt;/code&gt; on &lt;code dir=&quot;auto&quot;&gt;EXIT&lt;/code&gt; deletes &lt;code dir=&quot;auto&quot;&gt;/.snapshots/tmp-backup&lt;/code&gt; when the script finishes or is interrupted, so a &lt;code dir=&quot;auto&quot;&gt;Ctrl-C&lt;/code&gt; never leaves a stale snapshot behind. Only one transient snapshot ever exists; history lives on the vault drive, not in a growing pile of local snapshots. The cleanup guard checks that the variable is set and the path is still a directory, and any failure warning goes to stderr. No explicit &lt;code dir=&quot;auto&quot;&gt;exit&lt;/code&gt; is needed in the handler — &lt;code dir=&quot;auto&quot;&gt;EXIT&lt;/code&gt; already preserves the script’s exit status.&lt;/p&gt;
&lt;p&gt;This requires &lt;code dir=&quot;auto&quot;&gt;/home&lt;/code&gt; to be a Btrfs subvolume and &lt;code dir=&quot;auto&quot;&gt;/.snapshots&lt;/code&gt; to be writable by root.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;transfer-rsync-with-hard-link-incrementals&quot;&gt;Transfer: rsync with hard-link incrementals&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RSYNC_OPTS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;-aAXHS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--numeric-ids&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--filter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: .rsync-filter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--info&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;progress2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-e&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;RSYNC_OPTS&lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--link-dest&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;elif&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Warning: ignoring broken &apos;latest&apos; symlink for --link-dest.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&amp;#x26;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rsync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;RSYNC_OPTS&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TMP_SNAPSHOT_PATH&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;What each flag preserves or controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-a&lt;/code&gt; — archive mode: recursion, permissions, ownership, timestamps, symlinks, devices.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-A&lt;/code&gt; — ACLs, &lt;code dir=&quot;auto&quot;&gt;-X&lt;/code&gt; — extended attributes, &lt;code dir=&quot;auto&quot;&gt;-H&lt;/code&gt; — hard links, &lt;code dir=&quot;auto&quot;&gt;-S&lt;/code&gt; — sparse files. Without these, restores subtly differ from the original (broken SELinux labels, doubled disk usage for sparse VM images, split hard-linked files).&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--delete&lt;/code&gt; — makes each timestamped copy a full mirror of the source, not an ever-growing union of deleted files.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--numeric-ids&lt;/code&gt; — compares users/groups by UID/GID rather than name, so restores are correct even if the target system’s &lt;code dir=&quot;auto&quot;&gt;/etc/passwd&lt;/code&gt; differs.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--filter=&quot;: .rsync-filter&quot;&lt;/code&gt; — per-directory merge-file excludes, covered in detail in &lt;a href=&quot;#filtering-with-per-directory-rsync-filter-files&quot;&gt;Filtering with per-directory &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; files&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--info=progress2&lt;/code&gt; — single overall progress line instead of per-file noise.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--link-dest=&quot;$BACKUP_LATEST_PATH&quot;&lt;/code&gt; — the incremental mechanism. Unchanged files are hard-linked from the previous &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; backup instead of copied, so each timestamped directory looks like a full backup but only new or changed blocks consume space. It is only added when &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; is a symlink to an existing directory, which keeps the very first backup (no predecessor) working without special-casing. A dangling &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; symlink — for example after a manual delete — is ignored with a stderr warning instead of failing the &lt;code dir=&quot;auto&quot;&gt;rsync&lt;/code&gt; run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note the trailing slash in &lt;code dir=&quot;auto&quot;&gt;&quot;$TMP_SNAPSHOT_PATH/&quot;&lt;/code&gt; — it syncs the &lt;em&gt;contents&lt;/em&gt; of the snapshot into &lt;code dir=&quot;auto&quot;&gt;$BACKUP_PATH&lt;/code&gt; rather than nesting an extra &lt;code dir=&quot;auto&quot;&gt;tmp-backup&lt;/code&gt; directory inside it.&lt;/p&gt;
&lt;p&gt;After a successful transfer, &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; is repointed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sfn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;ln -sfn&lt;/code&gt; atomically replaces the symlink target, so &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; never dangles mid-update and &lt;code dir=&quot;auto&quot;&gt;--link-dest&lt;/code&gt; always has a valid reference for the next run.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;filtering-with-per-directory-rsync-filter-files&quot;&gt;Filtering with per-directory &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; files&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The single most effective backup-size control in this setup is not compression or deduplication — it is never copying regenerable noise in the first place. That is what this line enables:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;--filter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: .rsync-filter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;In rsync filter syntax, the &lt;code dir=&quot;auto&quot;&gt;:&lt;/code&gt; prefix means &lt;em&gt;dir-merge&lt;/em&gt;: before transferring the contents of every directory, rsync looks for a file named &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; inside it on the sending side and merges its rules into the active filter list. Key properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Distributed.&lt;/strong&gt; Rules live next to the files they describe instead of in one central exclude file passed on the command line. A project directory declares its own build outputs; a cache directory declares itself. Adding a new noisy tool to one directory never requires editing the backup script.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inherited.&lt;/strong&gt; Rules from a parent directory apply to all of its subdirectories. A subdirectory can add narrower rules, which take precedence over inherited ones because newer per-directory rules are evaluated first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Snapshot-aware.&lt;/strong&gt; Because the transfer source is the frozen snapshot at &lt;code dir=&quot;auto&quot;&gt;/.snapshots/tmp-backup&lt;/code&gt;, the filter files are read from the same point-in-time view as the data. A half-written exclude file cannot skew a run.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transferred by default.&lt;/strong&gt; No &lt;code dir=&quot;auto&quot;&gt;e&lt;/code&gt; modifier is used, so the &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; files themselves are part of the backup. Each timestamped copy therefore records exactly which rules produced it, and &lt;code dir=&quot;auto&quot;&gt;--delete&lt;/code&gt; stays consistent against the &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; reference.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;rule-pattern-basics&quot;&gt;Rule pattern basics&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Each line in a &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; file is a filter rule. The most common form is an exclude starting with &lt;code dir=&quot;auto&quot;&gt;- &lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /node_modules/***&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /packages/*/dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /packages/@sysopkit/*/dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /tests/fixtures/container/cache/*.tar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- tsconfig.tsbuildinfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These lines come from this repository’s own &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; and illustrate the full pattern vocabulary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt; marks the rule as an exclude (vs. &lt;code dir=&quot;auto&quot;&gt;+&lt;/code&gt; for an include).&lt;/li&gt;
&lt;li&gt;A leading &lt;code dir=&quot;auto&quot;&gt;/&lt;/code&gt; anchors the pattern to the directory containing the filter file. &lt;code dir=&quot;auto&quot;&gt;/node_modules/***&lt;/code&gt; excludes only the top-level &lt;code dir=&quot;auto&quot;&gt;node_modules&lt;/code&gt;, not a nested one deeper in the tree.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;***&lt;/code&gt; matches across directory boundaries including the directory itself — &lt;code dir=&quot;auto&quot;&gt;/node_modules/***&lt;/code&gt; excludes the directory and everything under it.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;*&lt;/code&gt; matches within a single path component — &lt;code dir=&quot;auto&quot;&gt;/packages/*/dist&lt;/code&gt; excludes the &lt;code dir=&quot;auto&quot;&gt;dist&lt;/code&gt; directory of every top-level package.&lt;/li&gt;
&lt;li&gt;No leading &lt;code dir=&quot;auto&quot;&gt;/&lt;/code&gt; means a floating match — &lt;code dir=&quot;auto&quot;&gt;tsconfig.tsbuildinfo&lt;/code&gt; excludes that filename at any depth below the filter file’s directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same vocabulary maps directly onto home-directory noise. Typical per-directory filter files in a &lt;code dir=&quot;auto&quot;&gt;/home&lt;/code&gt; tree look like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/projects/webapp/.rsync-filter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /node_modules/***&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /dist/***&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /.vite/***&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/.cache/.rsync-filter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# everything under here is regenerable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- ***&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/vm/.rsync-filter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /*.tmp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- /snapshots/***&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Each file is small, local, and obvious to whoever owns that directory. The backup script itself stays untouched as new directories appear — placing a &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; file is sufficient to opt out of backup traffic.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;interaction-with---delete-and---link-dest&quot;&gt;Interaction with &lt;code dir=&quot;auto&quot;&gt;--delete&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;--link-dest&lt;/code&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Excluded paths are hidden from the sender’s file list, so they are neither copied nor hard-linked into the new timestamped directory. With plain &lt;code dir=&quot;auto&quot;&gt;--delete&lt;/code&gt; (without &lt;code dir=&quot;auto&quot;&gt;--delete-excluded&lt;/code&gt;), files already present in older snapshots from before an exclude was added are left alone — history is immutable, which is the desired behavior — while new snapshots stop accumulating that noise. Old snapshots age out through the 60-day retention pass, so the saving compounds over time.&lt;/p&gt;
&lt;p&gt;A practical consequence: before the first backup of a new workstation, dropping a handful of &lt;code dir=&quot;auto&quot;&gt;.rsync-filter&lt;/code&gt; files for caches, &lt;code dir=&quot;auto&quot;&gt;node_modules&lt;/code&gt;, and build outputs typically cuts the transferred volume more than any rsync flag tuning. Verifying with &lt;code dir=&quot;auto&quot;&gt;rsync -n&lt;/code&gt; (dry run) against a snapshot shows exactly which paths the filters remove, without touching the vault drive.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;integrity-check-btrfs-scrub-of-the-vault-drive&quot;&gt;Integrity check: Btrfs scrub of the vault drive&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ $(&lt;/span&gt;&lt;span&gt;findmnt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-no&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FSTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-T&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Starting btrfs scrub on &lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;btrfs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scrub&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-B&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-q&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Scrub complete: No corruption detected on the backup drive.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;CRITICAL: Btrfs scrub detected errors on &lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Check &apos;dmesg&apos; or &apos;btrfs scrub status &lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;&apos; for details.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If the vault filesystem is Btrfs, every backup ends with a full scrub: all data and metadata checksums are re-read and verified (&lt;code dir=&quot;auto&quot;&gt;-B&lt;/code&gt; blocks until it finishes). This catches bit rot and a dying drive early, while older timestamped copies may still be intact. The &lt;code dir=&quot;auto&quot;&gt;findmnt&lt;/code&gt; guard keeps the script working when the vault drive is formatted with something else — scrub is skipped instead of failing.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;retention-timestamped-directories-with-time-based-expiry&quot;&gt;Retention: timestamped directories with time-based expiry&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RETENTION_DAYS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LATEST_TARGET&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;LATEST_TARGET&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;readlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_LATEST_PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$LATEST_TARGET&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mindepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-maxdepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mtime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$RETENTION_DAYS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$LATEST_TARGET&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BACKUP_ROOT_PATH&lt;/span&gt;&lt;span&gt;/backup/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mindepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-maxdepth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-mtime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$RETENTION_DAYS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Backups older than 60 days are deleted at the end of each run, keeping only top-level directories under &lt;code dir=&quot;auto&quot;&gt;backup/&lt;/code&gt;. Because unchanged files are hard-links shared between snapshots, deleting an old directory only frees blocks unique to it — shared blocks stay alive through their remaining links. &lt;code dir=&quot;auto&quot;&gt;-mtime +60&lt;/code&gt; keys off directory modification time, which matches the &lt;code dir=&quot;auto&quot;&gt;DATETIME&lt;/code&gt; naming scheme and avoids &lt;code dir=&quot;auto&quot;&gt;ctime&lt;/code&gt; bumps from metadata-only changes. The current &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; target is explicitly excluded via &lt;code dir=&quot;auto&quot;&gt;! -name&lt;/code&gt;, so a stale-but-current backup is never deleted when the vault was offline past retention — it stays available as the next &lt;code dir=&quot;auto&quot;&gt;--link-dest&lt;/code&gt; reference. &lt;code dir=&quot;auto&quot;&gt;-print&lt;/code&gt; lists what is removed, and &lt;code dir=&quot;auto&quot;&gt;--&lt;/code&gt; keeps &lt;code dir=&quot;auto&quot;&gt;rm&lt;/code&gt; safe against names starting with &lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The closing hint addresses long-term Btrfs behavior on the vault drive: heavy hard-link and delete churn fragments free space over time, and an occasional &lt;code dir=&quot;auto&quot;&gt;btrfs balance&lt;/code&gt; compacts it when writes get slow.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;restoring&quot;&gt;Restoring&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is no restore subcommand because there is nothing to decode — each backup is a plain tree. Restoring a single file is a plain copy out of the timestamped directory or &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt;. Restoring everything is a reverse &lt;code dir=&quot;auto&quot;&gt;rsync&lt;/code&gt; of the desired snapshot. Picking a dated directory instead of &lt;code dir=&quot;auto&quot;&gt;latest&lt;/code&gt; provides point-in-time recovery.&lt;/p&gt;
</content:encoded><category>backup</category><category>rsync</category><category>btrfs</category><category>tips</category><category>linux</category><category>bash</category></item></channel></rss>