<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Zfs on inetshell</title><link>https://inet.sh/en/tags/zfs/</link><description>Recent content in Zfs on inetshell</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 19 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/en/tags/zfs/index.xml" rel="self" type="application/rss+xml"/><item><title>Export a ZFS ZVOL to a RAW image</title><link>https://inet.sh/en/posts/zfs/zvol-raw-image/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/zvol-raw-image/</guid><description>dd if=/dev/zvol/pool/volume of=file.raw to dump a ZVOL (Proxmox VM disks) to RAW, and when to use qemu-img instead.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>ZVOL = block device: <code>dd if=/dev/zvol/pool/vol of=vm.raw</code>. Snapshot first if the VM is running.</p>
<p>On Proxmox a VM disk on ZFS is a <strong>ZVOL</strong> (<code>rpool/data/vm-101-disk-0</code>), not a file. To copy it to another hypervisor or a USB stick, dump a RAW.</p>
<p>Thread: <a href="https://forum.proxmox.com/threads/import-convert-export-raw-images-to-zfs-volume.21241/">Import/convert/export RAW images to ZFS volume</a>.</p>
<h2 id="snapshot-and-dd">Snapshot and dd</h2>
<p>If the VM is running, freeze the disk with a snapshot and read the snap (the <code>@snap</code> ZVOL shows up under <code>/dev/zvol/…</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs snapshot rpool/data/vm-101-disk-0@export
</span></span><span class="line"><span class="cl">dd <span class="k">if</span><span class="o">=</span>/dev/zvol/rpool/data/vm-101-disk-0@export <span class="nv">of</span><span class="o">=</span>/mnt/backup/vm-101.raw <span class="nv">bs</span><span class="o">=</span>1M <span class="nv">status</span><span class="o">=</span>progress
</span></span><span class="line"><span class="cl">zfs destroy rpool/data/vm-101-disk-0@export
</span></span></code></pre></td></tr></table>
</div>
</div><p>VM off, you can read the live zvol:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">dd <span class="k">if</span><span class="o">=</span>/dev/zvol/rpool/data/vm-101-disk-0 <span class="nv">of</span><span class="o">=</span>/mnt/backup/vm-101.raw <span class="nv">bs</span><span class="o">=</span>1M <span class="nv">status</span><span class="o">=</span>progress
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>bs=1M</code> matters: the 512 B default takes forever.</p>
<h2 id="qemu-img-qcow2--vmdk">qemu-img (qcow2 / vmdk)</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">qemu-img convert -p -f raw -O qcow2 /mnt/backup/vm-101.raw /mnt/backup/vm-101.qcow2
</span></span></code></pre></td></tr></table>
</div>
</div><p>Or straight from the zvol:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">qemu-img convert -p -f raw -O qcow2 /dev/zvol/rpool/data/vm-101-disk-0 vm-101.qcow2
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="the-other-way-raw--zvol">The other way (RAW → ZVOL)</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs create -s -V 32G rpool/data/vm-101-disk-0
</span></span><span class="line"><span class="cl">dd <span class="k">if</span><span class="o">=</span>file.raw <span class="nv">of</span><span class="o">=</span>/dev/zvol/rpool/data/vm-101-disk-0 <span class="nv">bs</span><span class="o">=</span>1M <span class="nv">status</span><span class="o">=</span>progress
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-s</code> = sparse. The RAW must not be larger than the zvol.</p>
<p>See also: <a href="/en/posts/zfs/basic-commands/">basic commands</a>.</p>
]]></content:encoded></item><item><title>Import a ZFS pool after a power outage</title><link>https://inet.sh/en/posts/zfs/import-after-power-outage/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/import-after-power-outage/</guid><description>Use zpool import -F and zpool import -f to import a dirty ZFS pool after a power cut, then scrub.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>Pool will not import after power loss: <code>zpool import -f</code>, then <code>-F</code> if you must rewind txgs, then <code>zpool scrub</code>.</p>
<p>After a power cut the pool is often still <strong>on the disks</strong> but <code>zpool status</code> is empty: it was left dirty-exported, or the host booted without it. Do not <code>zpool create</code> on those disks.</p>
<h2 id="see-what-is-there">See what is there</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool import
</span></span></code></pre></td></tr></table>
</div>
</div><p>Lists importable pools by name (<code>VOL1</code> below) and whether they are <code>FAULTED</code> / <code>UNAVAIL</code>.</p>
<h2 id="import">Import</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool import -f VOL1
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-f</code> means “I know it might still look imported elsewhere”. That is the usual crash case: ZFS thinks the other side still holds it.</p>
<p>If <code>-f</code> is not enough because the last txgs were half-written:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool import -F VOL1
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-F</code> <strong>rewinds</strong> to an earlier consistent txg. You can lose the last seconds of writes. That is better than a pool that will not mount.</p>
<p>Dry-run:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool import -F -n VOL1
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="afterwards-scrub">Afterwards: scrub</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool scrub VOL1
</span></span><span class="line"><span class="cl">zpool status VOL1
</span></span></code></pre></td></tr></table>
</div>
</div><p>Scrub confirms the rewind did not leave checksum errors. Let it finish before you delete “just in case” snapshots.</p>
<h2 id="do-not">Do not</h2>
<ul>
<li><code>zpool create</code> on the same <code>sdX</code> devices wipes the labels.</li>
<li><code>-F</code> is not the first knob: try <code>-f</code> first.</li>
<li>If the pool is the root (<code>rpool</code> / Proxmox <code>bpool</code>), import from live media, not from the system that will not boot.</li>
</ul>
<p>See also: <a href="/en/posts/zfs/replace-disks/">replacing disks</a>, <a href="/en/posts/zfs/basic-commands/">basic commands</a>.</p>
]]></content:encoded></item><item><title>Native ZFS encryption (OpenZFS): create, inherit, change-key</title><link>https://inet.sh/en/posts/zfs/encryption/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/encryption/</guid><description>Create an encrypted OpenZFS dataset with a passphrase or keyfile, inherit encryption on children, and rotate keys with zfs change-key — not Solaris zfs key -c.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p><code>zfs create -o encryption=on …</code>. Children inherit. Rotate keys on Linux with <code>zfs change-key</code> (not Solaris <code>zfs key -c</code>).</p>
<p>OpenZFS encrypts <strong>datasets</strong>, not the whole pool. Pool labels and metaslabs stay visible; <code>tank/secret</code> contents do not. This is the TrueNAS / Proxmox / Linux path.</p>
<p>Background: <a href="https://arstechnica.com/gadgets/2021/06/a-quick-start-guide-to-openzfs-native-encryption/">Ars native encryption</a>, <a href="https://mtlynch.io/zfs-encrypted-backups/">ZFS encrypted backups</a>.</p>
<h2 id="create">Create</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs create -o <span class="nv">encryption</span><span class="o">=</span>on -o <span class="nv">keyformat</span><span class="o">=</span>passphrase -o <span class="nv">keylocation</span><span class="o">=</span>prompt tank/encrypted
</span></span><span class="line"><span class="cl">zfs create tank/encrypted/child1
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>child1</code> inherits encryption. Do not set <code>encryption=off</code> on a child: OpenZFS will not let you punch a plaintext hole under an encrypted parent.</p>
<p>Keyfile instead of prompt:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs create -o <span class="nv">encryption</span><span class="o">=</span>on -o <span class="nv">keyformat</span><span class="o">=</span>passphrase <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -o <span class="nv">keylocation</span><span class="o">=</span>file:///root/zfs.key tank/encrypted
</span></span></code></pre></td></tr></table>
</div>
</div><p>The key file does <strong>not</strong> live on the encrypted dataset. USB, TPM, or a path on another pool.</p>
<h2 id="load-the-key-at-boot">Load the key at boot</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs load-key tank/encrypted
</span></span><span class="line"><span class="cl">zfs mount tank/encrypted
</span></span></code></pre></td></tr></table>
</div>
</div><p>Without <code>load-key</code> the dataset exists and <code>zfs list</code> shows it, but it will not mount.</p>
<h2 id="receive-plaintext-as-encrypted">Receive plaintext as encrypted</h2>
<p>On the <strong>first</strong> <code>recv</code> you can set encryption properties:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs send tank/test@snap1 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> zfs recv -o <span class="nv">encryption</span><span class="o">=</span>on -o <span class="nv">keyformat</span><span class="o">=</span>passphrase <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      -o <span class="nv">keylocation</span><span class="o">=</span>file:///path/to/keyfile tank/encrypted
</span></span></code></pre></td></tr></table>
</div>
</div><p>That receive is <strong>without</strong> <code>-w</code>. Raw <code>-w</code> copies the source wrapping key: only valid if the source was already encrypted. Details: <a href="/en/posts/zfs/send-unencrypted-to-encrypted/">unencrypted send into encrypted</a>.</p>
<h2 id="rotate-the-passphrase-openzfs">Rotate the passphrase (OpenZFS)</h2>
<p>On <strong>Solaris</strong> the command was <code>zfs key -c</code>. On <strong>OpenZFS / Linux / TrueNAS</strong>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs change-key tank/encrypted
</span></span><span class="line"><span class="cl">zfs get keyformat,keylocation,keystatus tank/encrypted
</span></span></code></pre></td></tr></table>
</div>
</div><p>New keyfile:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs change-key -o <span class="nv">keylocation</span><span class="o">=</span>file:///media/stick/key tank/encrypted
</span></span></code></pre></td></tr></table>
</div>
</div><p>Wrapping-key rekey (does not rewrite every block; rotates the key that wraps master keys) is <code>zfs change-key -i</code> depending on version — read <strong>your</strong> <code>zfs change-key</code> man page before production.</p>
<p>See also: <a href="/en/posts/zfs/hold-protect-snapshot/">snapshot holds</a>, <a href="/en/posts/zfs/backups-to-s3/">off-host backups</a>.</p>
]]></content:encoded></item><item><title>Protect ZFS snapshots from destroy with hold</title><link>https://inet.sh/en/posts/zfs/hold-protect-snapshot/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/hold-protect-snapshot/</guid><description>zfs hold and zfs release: a keep tag on a snapshot makes zfs destroy fail with dataset is busy.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p><code>zfs hold keep pool/dataset@snap</code> — <code>zfs destroy</code> fails with <em>dataset is busy</em>. Release with <code>zfs release</code>.</p>
<p>Retention scripts (<code>zfs destroy -r …@auto-…</code>) do not ask. A <strong>hold</strong> is a named lock on a snapshot: <code>destroy</code> fails until you drop the tag.</p>
<p>Use it on the snapshot you are <code>zfs send</code>ing, or on the only known-good rollback after an upgrade.</p>
<h2 id="take-the-hold">Take the hold</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs hold keep tank/home/cindys@snap1
</span></span></code></pre></td></tr></table>
</div>
</div><p>Recursive on a whole tree (snapshot first, then hold):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs snapshot -r tank/home@now
</span></span><span class="line"><span class="cl">zfs hold -r keep tank/home@now
</span></span></code></pre></td></tr></table>
</div>
</div><p>The tag (<code>keep</code>) is yours, but <strong>unique per snapshot</strong>. You can have both <code>keep</code> and <code>offsite</code> on the same <code>@now</code>.</p>
<h2 id="what-destroy-does">What destroy does</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs destroy tank/home/cindys@snap1
</span></span><span class="line"><span class="cl"><span class="c1"># cannot destroy &#39;tank/home/cindys@snap1&#39;: dataset is busy</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>That is the hold, not a mounted filesystem. <code>zfs destroy -d</code> <strong>defers</strong> deletion until holds are gone; it does not override them.</p>
<h2 id="list">List</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs holds tank/home@now
</span></span><span class="line"><span class="cl">zfs holds -r tank/home@now
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">NAME                  TAG   TIMESTAMP
</span></span><span class="line"><span class="cl">tank/home@now         keep  Thu Jul 15 11:25:39 2010
</span></span><span class="line"><span class="cl">tank/home/cindys@now  keep  Thu Jul 15 11:25:39 2010
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="release-then-destroy">Release, then destroy</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs release -r keep tank/home@now
</span></span><span class="line"><span class="cl">zfs destroy -r tank/home@now
</span></span></code></pre></td></tr></table>
</div>
</div><p>If a long <code>send</code> is still running, wait: the hold exists so prune cannot eat your incremental cursor.</p>
<p>Oracle&rsquo;s description (same mechanism on OpenZFS): <a href="https://docs.oracle.com/cd/E19253-01/819-5461/gjdfk/index.html">Holding ZFS snapshots</a>.</p>
<p>See also: <a href="/en/posts/zfs/send-unencrypted-to-encrypted/">send into an encrypted dataset</a>.</p>
]]></content:encoded></item><item><title>Replace a disk in a ZFS pool</title><link>https://inet.sh/en/posts/zfs/replace-disks/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/replace-disks/</guid><description>zpool replace -f to swap an OFFLINE or GUID disk on ZFS on Linux / Proxmox, then wait for resilver.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>OFFLINE disk: <code>zpool replace -f &lt;GUID&gt; /dev/disk/by-id/…</code> — GUID from <code>zpool status</code>, not <code>/dev/sdX</code>.</p>
<p>Proxmox documents the case: <a href="https://pve.proxmox.com/wiki/ZFS_on_Linux#sysadmin_zfs_change_failed_dev">ZFS on Linux — change a failed device</a>. The symptom is an <code>OFFLINE</code> / <code>FAULTED</code> vdev and a long number instead of <code>sde</code>.</p>
<h2 id="status">Status</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool status vol1
</span></span></code></pre></td></tr></table>
</div>
</div><p>The dead member often shows up as a GUID:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">1894156996840098641  OFFLINE
</span></span></code></pre></td></tr></table>
</div>
</div><p>That happens when the kernel no longer has a <code>/dev/sdX</code> for that disk (you yanked it, it died, or the letter moved).</p>
<h2 id="replace">Replace</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool replace -f vol1 <span class="m">1894156996840098641</span> /dev/disk/by-id/ata-NEWDISK
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-f</code> forces if ZFS still “remembers” the old disk. The replacement must be <strong>at least</strong> the same size (prefer by-id, not <code>sde</code>).</p>
<p>If the new disk <strong>is</strong> in the same slot and ZFS sees it:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool replace vol1 /dev/disk/by-id/ata-OLDDISK /dev/disk/by-id/ata-NEWDISK
</span></span></code></pre></td></tr></table>
</div>
</div><p>or auto-detect a replacement in-place:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool replace vol1 /dev/disk/by-id/ata-OLDDISK
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="wait-for-resilver">Wait for resilver</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool status vol1
</span></span><span class="line"><span class="cl">watch -n <span class="m">5</span> zpool status vol1
</span></span></code></pre></td></tr></table>
</div>
</div><p>Do not reboot, export, or start another replace on the same vdev until <code>resilvered</code>. On RAIDZ1 you are one disk down: a second failure in that window is data loss.</p>
<h2 id="afterwards">Afterwards</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool detach vol1 <span class="m">1894156996840098641</span>   <span class="c1"># only if status still shows it as spare/old</span>
</span></span><span class="line"><span class="cl">zpool labelclear /dev/sdOLD             <span class="c1"># if you reuse the old disk elsewhere</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>See also: <a href="/en/posts/zfs/import-after-power-outage/">import after a power outage</a>.</p>
]]></content:encoded></item><item><title>Send ZFS snapshots to another host (and on to S3)</title><link>https://inet.sh/en/posts/zfs/backups-to-s3/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/backups-to-s3/</guid><description>zfs send -w -R -i over SSH into zfs recv -s, and how to land the same stream in S3. Incremental, raw, resumable.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p><code>zfs send -w -R -i</code> over SSH to <code>zfs recv -s</code>. The stream is not a tarball; S3 is an extra hop if needed.</p>
<p><code>zfs send</code> emits a <strong>replicable stream</strong>, not a directory. The natural sink is another pool (<code>zfs recv</code>). S3 is a second hop: store the stream as an object, or use something like <a href="https://github.com/pressly/z3">z3</a>.</p>
<h2 id="incremental-to-another-host-the-command-i-actually-run">Incremental to another host (the command I actually run)</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs send -w -R -v -i vol1/secure/backups@initial vol1/secure/backups@new <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> ssh root@10.0.0.1 zfs recv -s vol1/secure/backups
</span></span></code></pre></td></tr></table>
</div>
</div><table>
  <thead>
      <tr>
          <th>Flag</th>
          <th>Meaning</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>-w</code></td>
          <td>raw: send the dataset <strong>encrypted as-is</strong>. The receiver does not need the key to <code>recv</code>.</td>
      </tr>
      <tr>
          <td><code>-R</code></td>
          <td>replicate properties and child snapshots</td>
      </tr>
      <tr>
          <td><code>-i @initial @new</code></td>
          <td>incremental from <code>@initial</code> (must exist on <strong>both</strong> sides)</td>
      </tr>
      <tr>
          <td><code>-v</code></td>
          <td>progress</td>
      </tr>
      <tr>
          <td><code>recv -s</code></td>
          <td><strong>resumable</strong> if SSH drops (<code>recv -s</code> again)</td>
      </tr>
  </tbody>
</table>
<p><a href="/en/posts/zfs/hold-protect-snapshot/">Hold</a> <code>@initial</code> and <code>@new</code> while the send runs, or prune will break the chain.</p>
<p>The first full (no <code>-i</code>) is mandatory once:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs send -w -R -v vol1/secure/backups@initial <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> ssh root@10.0.0.1 zfs recv -s vol1/secure/backups
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="land-it-in-s3">Land it in S3</h2>
<p>Same stream, other side of the pipe:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs send -w -R vol1/secure/backups@new <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> gzip -1 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> aws s3 cp - s3://my-bucket/zfs/backups@new.zfs.gz
</span></span></code></pre></td></tr></table>
</div>
</div><p>Restore:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">aws s3 cp s3://my-bucket/zfs/backups@new.zfs.gz - <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> gunzip <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> zfs recv -s vol1/secure/backups
</span></span></code></pre></td></tr></table>
</div>
</div><p>This is <strong>not</strong> a file sync. It is an opaque blob: you restore the whole stream (or the incremental on top of the full), or you restore nothing. Name <code>@initial</code> / <code>@new</code> and do not delete the full.</p>
<p>For incrementals in S3, tools like z3 keep a catalog of what already landed. A raw <code>aws s3 cp -</code> does not.</p>
<p><code>-w</code> into S3 is correct if the dataset is already encrypted: AWS never sees plaintext. If the source is <strong>not</strong> encrypted, either encrypt on recv (<a href="/en/posts/zfs/send-unencrypted-to-encrypted/">post</a>) or encrypt the object (KMS / client) — a cleartext stream in a bucket is a cleartext backup.</p>
<p>See also: <a href="/en/posts/zfs/encryption/">native encryption</a>.</p>
]]></content:encoded></item><item><title>ZFS command cheat sheet (pool, compression, cache, log)</title><link>https://inet.sh/en/posts/zfs/basic-commands/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/basic-commands/</guid><description>ZFS cheat sheet: zpool create raidz, export/import, lz4 compression, L2ARC, SLOG, labelclear, and destroy.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>Cheat sheet: <code>zpool create</code>, export/import, <code>lz4</code>, L2ARC, SLOG, <code>labelclear</code>, destroy — the 2 a.m. set.</p>
<p>Short reference. Longer procedures are linked at the bottom.</p>
<h2 id="pool">Pool</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool create vol1 raidz sda sdb sdc
</span></span><span class="line"><span class="cl">zpool status vol1
</span></span><span class="line"><span class="cl">zpool <span class="nb">export</span> vol1
</span></span><span class="line"><span class="cl">zpool import -f vol1
</span></span><span class="line"><span class="cl">zpool destroy tank          <span class="c1"># irreversible</span>
</span></span><span class="line"><span class="cl">zpool labelclear ada0       <span class="c1"># wipe ZFS labels from a spare disk</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>raidz</code> here is RAIDZ1 (one parity). For two, <code>raidz2</code>. Address disks as <code>/dev/disk/by-id/…</code>, not <code>sdX</code>, which reorder.</p>
<h2 id="mountpoint">Mountpoint</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs unmount vol1
</span></span><span class="line"><span class="cl">zfs <span class="nb">set</span> <span class="nv">mountpoint</span><span class="o">=</span>/mnt/vol1 vol1
</span></span><span class="line"><span class="cl">zfs mount vol1
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="l2arc-cache-and-slog-zil">L2ARC (cache) and SLOG (ZIL)</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool add vol1 cache &lt;disk-id&gt;
</span></span><span class="line"><span class="cl">zpool add vol1 log &lt;disk-id&gt;
</span></span><span class="line"><span class="cl">zpool add vol1 log mirror &lt;disk-id-a&gt; &lt;disk-id-b&gt;
</span></span><span class="line"><span class="cl">zpool remove vol1 &lt;device&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p>Cache = hot reads. Log = sync writes (NFS, VMs). A “fast” USB SLOG is worse than no SLOG: if it dies mid-txg, you feel it. Mirror the log.</p>
<h2 id="compression">Compression</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs <span class="nb">set</span> <span class="nv">compression</span><span class="o">=</span>lz4 newvol
</span></span><span class="line"><span class="cl">zfs <span class="nb">set</span> <span class="nv">compression</span><span class="o">=</span>off newvol
</span></span><span class="line"><span class="cl">zfs get compressratio newvol
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>lz4</code> is the sane default on modern OpenZFS. <code>zstd</code> compresses more and costs CPU. The property inherits to child datasets that do not override it.</p>
<h2 id="see-also">See also</h2>
<ul>
<li><a href="/en/posts/zfs/import-after-power-outage/">Import after a power outage</a></li>
<li><a href="/en/posts/zfs/replace-disks/">Replace disks</a></li>
<li><a href="/en/posts/zfs/encryption/">Native encryption</a></li>
</ul>
]]></content:encoded></item><item><title>zfs send/recv from an unencrypted dataset into an encrypted one</title><link>https://inet.sh/en/posts/zfs/send-unencrypted-to-encrypted/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/send-unencrypted-to-encrypted/</guid><description>Migrate a plaintext ZFS dataset onto an encrypted one with zfs send | zfs recv -o encryption=on on the initial receive.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>First <code>recv</code> with <code>-o encryption=on</code> (no <code>-w</code>). Destination dataset name is required.</p>
<p>Same bytes, new key. Source stays plaintext; destination is born encrypted. OpenZFS allows this <strong>only on the initial receive</strong> (dataset that does not exist yet).</p>
<p>Source thread: <a href="https://www.reddit.com/r/zfs/comments/mus2gn/zfs_sendreceive_from_enencrypted_dataset_to/">r/zfs</a>.</p>
<h2 id="command">Command</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs snapshot tank/test@snap1
</span></span><span class="line"><span class="cl">zfs send tank/test@snap1 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> zfs recv -o <span class="nv">encryption</span><span class="o">=</span>on <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      -o <span class="nv">keyformat</span><span class="o">=</span>passphrase <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      -o <span class="nv">keylocation</span><span class="o">=</span>file:///path/to/keyfile <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      tank/encrypted
</span></span></code></pre></td></tr></table>
</div>
</div><p>Things that silently break the send:</p>
<ol>
<li><strong>Do not use <code>-w</code>.</strong> Raw send replicates the source encryption state. Plaintext in → plaintext out, and <code>-o encryption=…</code> is ignored or errors.</li>
<li><strong>Name the destination dataset</strong> (<code>tank/encrypted</code>). A <code>recv</code> with no target does not create it.</li>
<li><strong>Passphrase vs file:</strong> <code>keylocation=prompt</code> is a bad fit across a non-interactive pipe. Use a keyfile.</li>
<li>Later incrementals (<code>-i</code>) <strong>inherit</strong> encryption. Do not pass <code>-o encryption=on</code> again.</li>
</ol>
<h2 id="check">Check</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs get encryption,keystatus,keyformat tank/encrypted
</span></span><span class="line"><span class="cl">zfs load-key tank/encrypted   <span class="c1"># if keystatus=unavailable</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="the-other-direction">The other direction</h2>
<p>Encrypted → encrypted with the <strong>same</strong> wrapping key: <code>zfs send -w</code>. Encrypted → new wrapping key: send <strong>without</strong> <code>-w</code> (ZFS decrypts on send; recv encrypts). That needs <code>load-key</code> on the source and is slower.</p>
<p>See also: <a href="/en/posts/zfs/encryption/">native encryption</a>.</p>
]]></content:encoded></item></channel></rss>