<?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>Home on inetshell</title><link>https://inet.sh/en/</link><description>Recent content in Home on inetshell</description><generator>Hugo</generator><language>en</language><lastBuildDate>Thu, 20 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/en/index.xml" rel="self" type="application/rss+xml"/><item><title>About</title><link>https://inet.sh/en/about/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/about/</guid><description>Who I am, contact, and social links.</description><content:encoded><![CDATA[<h2 id="bio">Bio</h2>
<p><em>(Draft — short bio TBD.)</em></p>
<h2 id="contact">Contact</h2>
<ul>
<li>Personal email: <a class="obfuscated-email" data-e="bWVAaW5ldHNoZWxsLm14" href="#" rel="nofollow" title="email">me [at] inetshell [dot] mx</a>
</li>
<li>Work email: <a class="obfuscated-email" data-e="bWVAaW5ldC5zaA==" href="#" rel="nofollow" title="email">me [at] inet [dot] sh</a>
</li>
<li>GPG/PGP: TBD</li>
</ul>
<h2 id="social-networks">Social Networks</h2>
<p>Icons below link to the same accounts.</p>
]]></content:encoded></item><item><title>Browse the modern web on Windows XP and Server 2003</title><link>https://inet.sh/en/posts/windows/modern-browser-xp-2003/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/windows/modern-browser-xp-2003/</guid><description>How to open current HTTPS sites on Windows XP SP3 or Server 2003 SP2 when Chrome, Firefox, and IE no longer work: Supermium, a Chromium build for legacy Windows.</description><content:encoded><![CDATA[<p>Microsoft ended support for <strong>Windows XP</strong> in April 2014 and for <strong>Windows Server 2003 / 2003 R2</strong> in July 2015. No official patches, no Schannel/TLS updates, and almost no vendor still tests software on those platforms.</p>
<p>In practice you cannot “just open Internet Explorer.” IE 8 speaks TLS 1.0 (maybe 1.2 with brittle hacks). <strong>Google Chrome</strong> dropped XP/Vista years ago; current installers will not even start. <strong>Mozilla Firefox</strong> left XP as well (only ancient ESR builds were usable). Banks, GitHub, cloud consoles, and most SaaS demand TLS 1.2+ and a recent Chromium. On a lab ProLiant still running 2003 you get blank pages, certificate errors, or “cannot display the webpage.”</p>
<p>If you still have to touch a legacy host (firmware, SCADA, a license server, a lab box), you need a <strong>modern</strong> browser that still builds for old Win32. The option that works in the lab: <strong><a href="https://www.win32subsystem.live/supermium/">Supermium</a></strong>.</p>
<h2 id="what-supermium-is">What Supermium is</h2>
<p>A <strong>Chromium</strong> fork aimed at Windows XP SP3, Server 2003 SP2, Vista, 7, 8.x, and newer. It behaves like a Chrome drop-in (extensions, <code>chrome://flags</code>, sandbox) but still ships 32-bit SSE2 builds for CPUs from that era.</p>
<p>Official requirements (short):</p>
<ul>
<li>Windows XP SP3 <strong>or</strong> Server 2003 SP2 (or later)</li>
<li>An <strong>SSE2</strong>-capable CPU (Pentium 4 or newer; dual-core recommended)</li>
<li>≥ 768 MB RAM (2 GB+ if you want it usable)</li>
</ul>
<p>Downloads: <a href="https://www.win32subsystem.live/supermium/">win32subsystem.live/supermium</a> — 32-bit and 64-bit setups.</p>
<h2 id="quick-install">Quick install</h2>
<ol>
<li>On a machine with a <strong>modern</strong> browser, download the 32-bit Setup (most lab XP/2003 is x86) or 64-bit if the OS is x64.</li>
<li>Copy the installer to the legacy host (USB, SMB, iLO virtual media).</li>
<li>Install as Administrator.</li>
<li>Open Supermium and hit <code>https://example.com</code> plus the site you actually need.</li>
</ol>
<p>If the installer will not run: confirm SP3/SP2, SSE2, and that you are not on Windows 2000 / XP RTM without a service pack.</p>
<h2 id="extensions-and-privacy">Extensions and privacy</h2>
<p>Unlike current Chrome, Supermium is <strong>not</strong> killing Manifest V2 on a schedule. You can sideload <a href="https://github.com/gorhill/uBlock">uBlock Origin</a> from GitHub and keep ads out on an OS that should not be on the public Internet.</p>
<p>It also carries ungoogled-chromium-style flags and classic UI options (Aero, old tabs). Nice; do not treat that as OS hardening.</p>
<h2 id="warnings-read-them">Warnings (read them)</h2>
<ul>
<li><strong>The OS is still unpatched.</strong> A new browser does not fix SMB1, old RDP, or kernel bugs. Keep the host on an isolated VLAN, VPN, or jump box; do not expose it to the Internet.</li>
<li>Supermium fixes <em>sites</em> broken by TLS; it does <strong>not</strong> make XP/2003 a safe daily driver.</li>
<li>Download only from the official site. Random “Chrome for XP” forum builds are a malware classic.</li>
<li>For real work the goal is still <strong>migration</strong> (new VM, container, or move the service to a supported OS). This is a bridge, not a strategy.</li>
</ul>
<h2 id="when-it-is-worth-it">When it is worth it</h2>
<ul>
<li>Opening an iLO / iDRAC / appliance UI you only documented on a 2003 lab box.</li>
<li>Grabbing a driver or EULA from a portal that rejects IE8.</li>
<li>Reproducing a compatibility failure without spinning up Windows 10 for one click.</li>
</ul>
<p>Download and docs: <a href="https://www.win32subsystem.live/supermium/">Supermium</a>.</p>
]]></content:encoded></item><item><title>Move k3s data to another disk</title><link>https://inet.sh/en/posts/k3s/migrate-data/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/k3s/migrate-data/</guid><description>How to move /var/lib/rancher, kubelet pods, and /run/k3s to another disk or partition: stop, mv, symlink, start. /run tmpfs traps and the --data-dir alternative.</description><content:encoded><![CDATA[<p>k3s keeps state on the system disk. When <code>/</code> fills up or you want faster storage, the short path is: <strong>stop → <code>mv</code> → symlink → start</strong>.</p>
<p>The destination (<code>/datadrive</code> here) must exist and be mounted <strong>first</strong>. Root or sudo.</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">sudo systemctl stop k3s
</span></span><span class="line"><span class="cl">sudo systemctl stop k3s-agent   <span class="c1"># only if this node is an agent</span>
</span></span><span class="line"><span class="cl">sudo /usr/local/bin/k3s-killall.sh
</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-shell" data-lang="shell"><span class="line"><span class="cl">sudo mv /run/k3s/ /datadrive/k3s/
</span></span><span class="line"><span class="cl">sudo mv /var/lib/kubelet/pods/ /datadrive/k3s-pods/
</span></span><span class="line"><span class="cl">sudo mv /var/lib/rancher/ /datadrive/k3s-rancher/
</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-shell" data-lang="shell"><span class="line"><span class="cl">sudo ln -s /datadrive/k3s/ /run/k3s
</span></span><span class="line"><span class="cl">sudo ln -s /datadrive/k3s-pods/ /var/lib/kubelet/pods
</span></span><span class="line"><span class="cl">sudo ln -s /datadrive/k3s-rancher/ /var/lib/rancher
</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo systemctl start k3s
</span></span><span class="line"><span class="cl">sudo systemctl start k3s-agent   <span class="c1"># agents only</span>
</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl get nodes
</span></span><span class="line"><span class="cl">kubectl get pods -A
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="what-moves">What moves</h2>
<table>
  <thead>
      <tr>
          <th>Source</th>
          <th>Example destination</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>/run/k3s/</code></td>
          <td><code>/datadrive/k3s/</code></td>
      </tr>
      <tr>
          <td><code>/var/lib/kubelet/pods/</code></td>
          <td><code>/datadrive/k3s-pods/</code></td>
      </tr>
      <tr>
          <td><code>/var/lib/rancher/</code></td>
          <td><code>/datadrive/k3s-rancher/</code></td>
      </tr>
  </tbody>
</table>
<p>Working state lives there: containerd, manifests, pods. A half-finished <code>mv</code> or a start against empty paths breaks the cluster.</p>
<h2 id="traps">Traps</h2>
<p><strong><code>/run</code> is tmpfs.</strong> After a reboot the <code>/run/k3s</code> symlink is gone. Recreate it from a unit <code>After=local-fs.target</code>, or leave <code>/run/k3s</code> on RAM and do not move it.</p>
<p><strong><code>k3s-agent</code> does not exist on a server-only node.</strong> <code>systemctl stop k3s-agent</code> failing is not a migration failure.</p>
<p><strong>Create the destination parent</strong> (<code>mkdir -p /datadrive</code>) and confirm the mount (<code>findmnt /datadrive</code>) <strong>before</strong> <code>mv</code>. A <code>mv</code> onto an unmounted path leaves the data on the old disk under a new name.</p>
<p><strong>containerd and kubelet sometimes reject a symlink.</strong> If the node never goes Ready, switch to a bind mount in <code>/etc/fstab</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">/</span><span class="n">datadrive</span><span class="o">/</span><span class="n">k3s</span><span class="o">-</span><span class="n">rancher</span>  <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">rancher</span>  <span class="n">none</span>  <span class="n">bind</span>  <span class="mi">0</span>  <span class="mi">0</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="cleaner-alternative---data-dir">Cleaner alternative: <code>--data-dir</code></h2>
<p>The official k3s path is <code>--data-dir</code> (default <code>/var/lib/rancher/k3s</code>). On a <strong>new</strong> node, install with the data-dir already on the large disk. On an <strong>existing</strong> node, <code>mv</code> + symlink (or bind) of <code>/var/lib/rancher</code> is the shortcut; do not rewrite the unit mid-cluster without a plan.</p>
<p>Write-up of this <code>mv</code> + symlink sequence: <a href="https://devopskit.tech/en/posts/migrate-k3s-data/">How to Move K3s Data to a New Location</a>.</p>
]]></content:encoded></item><item><title>Netcat equivalent on Windows: Test-NetConnection</title><link>https://inet.sh/en/posts/windows/netcat-equivalent/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/windows/netcat-equivalent/</guid><description>The Windows equivalent of nc -v host port is Test-NetConnection (tnc): it tests whether a TCP port accepts a connection. TcpTestSucceeded True/False.</description><content:encoded><![CDATA[<p>On Linux the reflex for “is 1433 open?” is:</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">nc -v 10.1.1.1 <span class="m">1433</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>Windows</strong> does not ship <code>nc</code>. The closest PowerShell equivalent is a TCP connect test:</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-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">Test-NetConnection</span> <span class="n">-ComputerName</span> <span class="mf">10.1</span><span class="p">.</span><span class="py">1</span><span class="p">.</span><span class="py">1</span> <span class="n">-Port</span> <span class="mf">1433</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Short alias:</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-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">tnc</span> <span class="mf">10.1</span><span class="p">.</span><span class="py">1</span><span class="p">.</span><span class="py">1</span> <span class="n">-Port</span> <span class="mf">1433</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="what-to-look-at">What to look at</h2>
<p><code>TcpTestSucceeded</code>:</p>
<table>
  <thead>
      <tr>
          <th>Value</th>
          <th>Meaning</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>True</code></td>
          <td>Port is open and answering (same as <code>nc</code> connecting)</td>
      </tr>
      <tr>
          <td><code>False</code></td>
          <td>No connection (firewall, dead service, unreachable IP)</td>
      </tr>
  </tbody>
</table>
<p>More detail, closer to <code>nc -v</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">Test-NetConnection</span> <span class="n">-ComputerName</span> <span class="mf">10.1</span><span class="p">.</span><span class="py">1</span><span class="p">.</span><span class="py">1</span> <span class="n">-Port</span> <span class="mf">1433</span> <span class="n">-InformationLevel</span> <span class="n">Detailed</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="port-only-no-ping">Port only, no ping</h2>
<p><code>Test-NetConnection</code> also pings by default. TCP only:</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-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">Test-NetConnection</span> <span class="n">-ComputerName</span> <span class="mf">10.254</span><span class="p">.</span><span class="py">3</span><span class="p">.</span><span class="py">2</span> <span class="n">-Port</span> <span class="mf">1433</span> <span class="n">-WarningAction</span> <span class="n">SilentlyContinue</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">  <span class="nb">Select-Object</span> <span class="n">ComputerName</span><span class="p">,</span> <span class="n">RemoteAddress</span><span class="p">,</span> <span class="n">RemotePort</span><span class="p">,</span> <span class="n">TcpTestSucceeded</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="very-old-powershell">Very old PowerShell</h2>
<p>If <code>Test-NetConnection</code> is missing, use the .NET TCP client:</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><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$tcp</span> <span class="p">=</span> <span class="nb">New-Object</span> <span class="n">System</span><span class="p">.</span><span class="py">Net</span><span class="p">.</span><span class="py">Sockets</span><span class="p">.</span><span class="py">TcpClient</span>
</span></span><span class="line"><span class="cl"><span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$tcp</span><span class="p">.</span><span class="py">Connect</span><span class="p">(</span><span class="s2">&#34;10.254.3.2&#34;</span><span class="p">,</span> <span class="mf">1433</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Write-Host</span> <span class="s2">&#34;Connected to 10.254.3.2:1433&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Write-Host</span> <span class="s2">&#34;Failed: </span><span class="p">$(</span><span class="nv">$_</span><span class="p">.</span><span class="py">Exception</span><span class="p">.</span><span class="n">Message</span><span class="p">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$tcp</span><span class="p">.</span><span class="py">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded></item><item><title>Resume</title><link>https://inet.sh/en/resume/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/resume/</guid><description>Resume and skills — draft.</description><content:encoded><![CDATA[<h2 id="skills">Skills</h2>
<ul>
<li>Kubernetes</li>
<li>Linux</li>
<li>AWS</li>
<li>GCP</li>
</ul>
]]></content:encoded></item><item><title>SSH into Windows Server 2003 with Bitvise</title><link>https://inet.sh/en/posts/windows/ssh-server-2003-bitvise/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/windows/ssh-server-2003-bitvise/</guid><description>How to get SSH on Windows Server 2003 or 2003 R2: Microsoft OpenSSH is not an option; Bitvise SSH Server (7.x in the lab) provides shell and SFTP.</description><content:encoded><![CDATA[<p><strong>Windows Server 2003 / 2003 R2</strong> does not ship an SSH server. Microsoft never shipped <strong>OpenSSH for Windows</strong> for that generation either (that arrived much later on Windows 10 / Server 2019+). PowerShell Remoting / WinRM is not the answer on 2003 either: the usable remote stack is RDP, SMB, and Telnet if you enabled it.</p>
<p>If you need <code>ssh user@2003-box</code> from a modern jump host (scripts, <code>scp</code>, legacy Ansible, pull logs without RDP), install a <strong>third-party SSH server</strong>. In the lab I use <strong><a href="https://bitvise.com/ssh-server-download">Bitvise SSH Server</a></strong>; <strong>7.x</strong> is the line validated on Server 2003.</p>
<p>Bitvise still lists <a href="https://bitvise.com/winsshd">XP SP3 and Server 2003</a> in its compatibility matrix. On a host unpatched since 2015, stick to an installer <strong>you have already validated</strong> (7.x if that is your golden image) or try current only on a clone — not blind on production.</p>
<h2 id="why-not-the-alternatives">Why not the alternatives</h2>
<table>
  <thead>
      <tr>
          <th>Option</th>
          <th>On Server 2003</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>OpenSSH (Win32-OpenSSH / Windows feature)</td>
          <td>No</td>
      </tr>
      <tr>
          <td>Cygwin <code>sshd</code></td>
          <td>Possible, brittle and heavy</td>
      </tr>
      <tr>
          <td>FreeSSHd / abandoned forks</td>
          <td>Avoid (unmaintained, attack surface)</td>
      </tr>
      <tr>
          <td>RDP only</td>
          <td>Fine for GUI; useless for SSH automation</td>
      </tr>
      <tr>
          <td>Bitvise SSH Server</td>
          <td>Yes — shell + SFTP on old Win32</td>
      </tr>
  </tbody>
</table>
<h2 id="install-short">Install (short)</h2>
<ol>
<li>On a machine with a modern browser, download the installer from <a href="https://bitvise.com/ssh-server-download">bitvise.com/ssh-server-download</a> (or the <strong>7.x</strong> package you keep for the lab).</li>
<li>Check the digital signature on the <code>.exe</code> (Properties → Digital Signatures → Bitvise).</li>
<li>Copy the installer to the 2003 box (USB, SMB, iLO virtual media). <strong>Do not</strong> fetch it with IE8 from a random mirror.</li>
<li>Run the installer <strong>as Administrator</strong>.</li>
<li>Reboot when Bitvise asks (recommended after first install, especially for key auth).</li>
<li>Open the <strong>Bitvise SSH Server Control Panel</strong> and start the service if it is not set to Automatic.</li>
</ol>
<p>Official install notes: <a href="https://bitvise.com/getting-started-install-ssh-server">Installing Bitvise SSH Server</a>.</p>
<h2 id="minimum-config-you-should-not-skip">Minimum config you should not skip</h2>
<ul>
<li><strong>Accounts:</strong> allow only the Windows user(s) you need (or Bitvise virtual accounts). Do not leave “any local account.”</li>
<li><strong>Port:</strong> 22 by default. On a management VLAN that is fine; otherwise change it <strong>and</strong> restrict with the host firewall / switch ACL.</li>
<li><strong>Windows 2003 firewall:</strong> open TCP/22 (or your chosen port) for the management network only.</li>
<li><strong>Key auth:</strong> import the jump-host pubkey; turn off password auth once keys work.</li>
<li><strong>SFTP root:</strong> mount only the path you need (e.g. <code>D:\logs</code>), not <code>C:\</code>.</li>
</ul>
<p>From the client:</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">ssh Administrator@192.168.x.x
</span></span><span class="line"><span class="cl"><span class="c1"># or whichever account you enabled</span>
</span></span><span class="line"><span class="cl">scp logfile.log Administrator@192.168.x.x:D:/logs/
</span></span></code></pre></td></tr></table>
</div>
</div><p>A modern OpenSSH client talks to Bitvise fine; you do not need the Bitvise client unless you want its GUI.</p>
<h2 id="security-legacy-context">Security (legacy context)</h2>
<p>The OS is <strong>still</strong> without Microsoft patches. Adding SSH improves operations; it <strong>increases</strong> network surface if the port is reachable beyond the jump host.</p>
<ul>
<li>VLAN / firewall: bastion IP only.</li>
<li>Do not put the 2003 box on the Internet “because it has SSH now.”</li>
<li>Licensing: Personal Edition is free for non-commercial use; Standard is eval / paid. Respect that in lab vs work.</li>
<li>Real goal: migrate the workload. SSH is the bridge to pull data and retire the box.</li>
</ul>
<h2 id="related">Related</h2>
<p>To open a modern HTTPS portal from the same host (IE8 will not cut it): <a href="/en/posts/windows/modern-browser-xp-2003/">modern browser on XP/2003 with Supermium</a>.</p>
<p>Vendor docs and downloads: <a href="https://bitvise.com/ssh-server-download">Bitvise SSH Server</a>.</p>
]]></content:encoded></item><item><title>Topics</title><link>https://inet.sh/en/topics/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/topics/</guid><description>Runbook topics: ZFS, HP iLO, Proxmox, Windows, and k3s.</description><content:encoded><![CDATA[<p><strong>Topics</strong> are the site folders (ZFS, iLO, …). They are not the same as <em>tags</em>: a tag is a detail (<code>encryption</code>, <code>bitvise</code>); a topic is the whole family.</p>
<h2 id="topics">Topics</h2>
<ul>
<li><a href="/en/posts/zfs/">ZFS</a> — pools, snapshots, encryption, send/recv</li>
<li><a href="/en/posts/ilo/">HP iLO</a> — CLI, firmware, license key</li>
<li><a href="/en/posts/proxmox/">Proxmox</a> — cloud-init templates and VMs</li>
<li><a href="/en/posts/windows/">Windows</a> — legacy XP/2003, disk, SSH</li>
<li><a href="/en/posts/k3s/">k3s</a> — lightweight cluster, data-dir, disk moves</li>
</ul>
<h2 id="tags">Tags</h2>
<p>For a loose keyword (extension, tool, symptom): <a href="/en/tags/">all tags</a>.</p>
]]></content:encoded></item><item><title>WinDirStat: see what is eating the disk on Windows</title><link>https://inet.sh/en/posts/windows/windirstat-disk-usage/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/windows/windirstat-disk-usage/</guid><description>WinDirStat is a Windows disk usage analyzer: like du, but with a treemap, extension stats, and duplicate detection. Download at windirstat.net.</description><content:encoded><![CDATA[<p>On Linux, when a disk fills up, the reflex is:</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">du -xh --max-depth<span class="o">=</span><span class="m">1</span> / <span class="p">|</span> sort -h
</span></span><span class="line"><span class="cl"><span class="c1"># or ncdu /</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>On <strong>Windows</strong> there is no decent <code>du</code> in the box. Explorer → Properties is slow and opaque. The tool I use in the lab and on desktops: <strong><a href="https://windirstat.net/">WinDirStat</a></strong> — free, open source, interactive treemap.</p>
<p>Think of it as <code>du</code> on steroids: scan a drive or folder and see <strong>where</strong> the space went, not only how many GB.</p>
<h2 id="what-it-does-beyond-summing-bytes">What it does (beyond summing bytes)</h2>
<table>
  <thead>
      <tr>
          <th>View</th>
          <th>Use</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Directory list</td>
          <td>Sort by size, like <code>du | sort -h</code></td>
      </tr>
      <tr>
          <td>Extension stats</td>
          <td>See whether <code>.iso</code>, <code>.vmdk</code>, <code>.log</code>, or <code>.bak</code> dominate</td>
      </tr>
      <tr>
          <td>Treemap</td>
          <td>Each rectangle is a file; area is size; color is type</td>
      </tr>
      <tr>
          <td>Largest files / duplicates</td>
          <td>Obvious delete-or-move candidates</td>
      </tr>
  </tbody>
</table>
<p>The <a href="https://windirstat.net/">project site</a> documents multithreaded scans, direct NTFS scanning when available, dark mode, and Explorer integration.</p>
<h2 id="quick-use">Quick use</h2>
<ol>
<li>Download the installer (or portable build) from <a href="https://windirstat.net/">windirstat.net</a> / the project&rsquo;s GitHub releases.</li>
<li>Run <strong>as Administrator</strong> if you are scanning all of <code>C:\</code> (permissions on <code>Windows</code>, other user profiles, etc.).</li>
<li>Pick a volume or a specific folder (<code>D:\VMs</code>, <code>C:\Users\…\AppData</code>).</li>
<li>Wait for the scan. Large disks take time; this is not <code>du</code> on a tmpfs.</li>
<li>In the treemap, zoom into the fat block. Open the folder in Explorer, or delete from the UI only when you know what it is.</li>
</ol>
<h2 id="when-to-use-it">When to use it</h2>
<ul>
<li>Disk is “full” and you do not know whether it is Downloads, hibernation, pagefile, ISOs, or VM snapshots.</li>
<li>Before/after cleanup of a profile or a share.</li>
<li>Showing a non-CLI person what owns the space (the treemap sells itself).</li>
</ul>
<h2 id="what-it-is-not">What it is not</h2>
<ul>
<li>It does not replace <code>du</code>/<code>ncdu</code> on Linux or on a NAS over SSH.</li>
<li>It is not a magic cleaner: deleting System Volume Information, WinSxS, or “big” databases because they look large will break the host.</li>
<li>A full <code>C:\</code> scan on a file server with millions of files can take a long time; narrow the root.</li>
</ul>
<h2 id="mental-map">Mental map</h2>
<table>
  <thead>
      <tr>
          <th>World</th>
          <th>Tool</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Linux CLI</td>
          <td><code>du</code>, <code>ncdu</code></td>
      </tr>
      <tr>
          <td>Windows GUI</td>
          <td><strong>WinDirStat</strong></td>
      </tr>
      <tr>
          <td>macOS</td>
          <td>DaisyDisk / OmniDiskSweeper (another family)</td>
      </tr>
  </tbody>
</table>
<p>Download and docs: <a href="https://windirstat.net/">https://windirstat.net/</a>.</p>
]]></content:encoded></item><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[<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>HP iLO CLI: users, network, ISO, and power</title><link>https://inet.sh/en/posts/ilo/cli-commands/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/ilo/cli-commands/</guid><description>HP iLO SMASH CLP commands over SSH: create a user, change the password, set a static IP, mount an HTTP ISO, and power the server.</description><content:encoded><![CDATA[<p>If you have SSH to iLO and do not want the web UI, the shell is <strong>SMASH CLP</strong> (<code>/map1</code>, <code>oemhp_*</code>). This is the iLO 2/3/4 dialect, not Redfish.</p>
<h2 id="connect">Connect</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">ssh Administrator@&lt;ilo-ip&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p>Old firmware often needs a legacy cipher:</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">ssh -c aes256-cbc Administrator@&lt;ilo-ip&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="accounts">Accounts</h2>
<p>Create a user with the usual admin groups (network changes reset iLO; accounts persist):</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">create /map1/accounts1 <span class="nv">username</span><span class="o">=</span>ops <span class="nv">password</span><span class="o">=</span><span class="s1">&#39;&lt;password&gt;&#39;</span> <span class="nv">group</span><span class="o">=</span>admin,config,oemhp_vm,oemhp_rc,oemhp_power
</span></span></code></pre></td></tr></table>
</div>
</div><p>Change the <code>Administrator</code> password on first boot (<code>Administrator</code> / <code>hpinvent</code> is the HP default):</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"><span class="nb">set</span> /map1/accounts1/Administrator <span class="nv">password</span><span class="o">=</span><span class="s1">&#39;&lt;new-password&gt;&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="network-ilo-reboots">Network (iLO reboots)</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"><span class="nb">set</span> map1/dhcpendpt1 <span class="nv">EnabledState</span><span class="o">=</span>no
</span></span><span class="line"><span class="cl"><span class="nb">set</span> map1/enetport1/lanendpt1/ipendpt1 <span class="nv">IPv4Address</span><span class="o">=</span>192.168.1.213 <span class="nv">SubnetMask</span><span class="o">=</span>255.255.255.0
</span></span><span class="line"><span class="cl"><span class="nb">set</span> map1/enetport1 <span class="nv">SystemName</span><span class="o">=</span>merlin
</span></span></code></pre></td></tr></table>
</div>
</div><p>After each network <code>set</code>, wait for iLO to come back and SSH to the new address.</p>
<h2 id="mount-an-http-iso-and-boot-once">Mount an HTTP ISO and boot once</h2>
<p>The ISO must be HTTP-reachable <strong>from iLO</strong>, not from your laptop.</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"><span class="nb">set</span> /map1/oemhp_vm1/cddr1 <span class="nv">oemhp_image</span><span class="o">=</span>http://10.254.0.50/ssp2017.iso
</span></span><span class="line"><span class="cl"><span class="nb">set</span> /map1/oemhp_vm1/cddr1 <span class="nv">oemhp_boot</span><span class="o">=</span>Once
</span></span><span class="line"><span class="cl">show /map1/oemhp_vm1/cddr1
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="power">Power</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">show /system1
</span></span><span class="line"><span class="cl">start /system1
</span></span><span class="line"><span class="cl">stop /system1
</span></span><span class="line"><span class="cl">reset /system1
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="ping-from-ilo">Ping from iLO</h2>
<p>Useful to see whether iLO can reach the management network:</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">oemhp_ping /map1 10.1.1.50
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="manual">Manual</h2>
<p>HP&rsquo;s CLP tree: <a href="http://h10032.www1.hp.com/ctg/Manual/c02237707.pdf">iLO scripting and command line</a>.</p>
<p>See also: <a href="/en/posts/ilo/upgrade-firmware-ssh/">upgrade firmware over SSH</a>, <a href="/en/posts/ilo/get-license-key/">read the license key</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[<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[<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[<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>Proxmox templates from Linux cloud images (qm + cloud-init)</title><link>https://inet.sh/en/posts/proxmox/cloud-image-templates/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/proxmox/cloud-image-templates/</guid><description>Build a Proxmox template from a GenericCloud qcow2: wget, qm create, import-from, cloud-init, qm clone. Alma, Rocky, Ubuntu, and more.</description><content:encoded><![CDATA[<p>You do not need an installer ISO if you just want Linux that boots with a user, an SSH key, and DHCP. Distros ship a <strong>GenericCloud / cloudimg</strong> (qcow2): import it, attach a cloud-init drive, turn it into a template.</p>
<p>Tested on <strong>Proxmox 8.0.4</strong>. The flow is this <a href="https://gist.github.com/inetshell/f1d0206d5319c11062845901e4f3d06b">gist</a> (fork of zidenis). Fedora 38 / Ubuntu 23.04 URLs go stale: point <code>wget</code> at the distro’s <strong>current</strong> cloud image.</p>
<h2 id="1-download-the-image">1. Download the image</h2>
<p>On the node, as root, on a storage with space (NFS, local, whatever you use):</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><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"><span class="nb">export</span> <span class="nv">IMAGES_PATH</span><span class="o">=</span><span class="s2">&#34;/mnt/pve/nfs-data/images/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">IMAGES_PATH</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2
</span></span><span class="line"><span class="cl">wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/CHECKSUM -O SHA256SUMS
</span></span><span class="line"><span class="cl">sha256sum -c SHA256SUMS --ignore-missing
</span></span></code></pre></td></tr></table>
</div>
</div><p>Other distros (uncomment one):</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><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</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"><span class="c1"># Amazon Linux 2 — https://cdn.amazonlinux.com/os-images/latest/</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wget https://cdn.amazonlinux.com/os-images/2.0.20230727.0/kvm/amzn2-kvm-2.0.20230727.0-x86_64.xfs.gpt.qcow2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># CentOS Stream 9</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wget https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Fedora (bump the version)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wget https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images/Fedora-Cloud-Base-38-1.6.x86_64.qcow2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Oracle Linux 9 — the .qcow is not qcow2; convert it</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wget https://yum.oracle.com/templates/OracleLinux/OL9/u2/x86_64/OL9U2_x86_64-kvm-b197.qcow</span>
</span></span><span class="line"><span class="cl"><span class="c1"># qemu-img convert -O qcow2 -o compat=0.10 OL9U2_x86_64-kvm-b197.qcow OL9U2_x86_64-kvm-b197.qcow2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># RHEL 9 — needs a logged-in download from access.redhat.com</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Rocky 9</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wget https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Ubuntu cloudimg</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="2-vm-and-cloud-init-variables">2. VM and cloud-init variables</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><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</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"><span class="nb">export</span> <span class="nv">QEMU_CPU_MODEL</span><span class="o">=</span><span class="s2">&#34;host&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">VM_CPU_SOCKETS</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">VM_CPU_CORES</span><span class="o">=</span><span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">VM_MEMORY</span><span class="o">=</span><span class="m">4098</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">VM_RESOURCE_POOL</span><span class="o">=</span><span class="s2">&#34;CustomResourcePool&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CLOUD_INIT_USER</span><span class="o">=</span><span class="s2">&#34;user&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CLOUD_INIT_SSHKEY</span><span class="o">=</span><span class="s2">&#34;/home/user/.ssh/id_rsa.pub&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CLOUD_INIT_IP</span><span class="o">=</span><span class="s2">&#34;dhcp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CLOUD_INIT_NAMESERVER</span><span class="o">=</span><span class="s2">&#34;1.1.1.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CLOUD_INIT_SEARCHDOMAIN</span><span class="o">=</span><span class="s2">&#34;example.com&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">TEMPLATE_ID</span><span class="o">=</span><span class="m">1001</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">VM_NAME</span><span class="o">=</span><span class="s2">&#34;alma9&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">VM_DISK_IMAGE</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">IMAGES_PATH</span><span class="si">}</span><span class="s2">/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>--cpu host</code> passes through the node CPU flags (faster; the template is not portable across CPU vendors). <code>VM_MEMORY=4098</code> is the gist value; 4096 is fine.</p>
<p>The cloud-init user is <strong>not</strong> <code>root</code>. The key must be the <code>.pub</code> you will use from your laptop.</p>
<h2 id="3-create-the-base-vm-and-import-the-disk">3. Create the base VM and import the disk</h2>
<p>Swap <code>local-lvm</code> and <code>vmbr0</code> for your real storage and bridge.</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><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</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">qm create <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> --name <span class="si">${</span><span class="nv">VM_NAME</span><span class="si">}</span> --cpu <span class="si">${</span><span class="nv">QEMU_CPU_MODEL</span><span class="si">}</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --sockets <span class="si">${</span><span class="nv">VM_CPU_SOCKETS</span><span class="si">}</span> --cores <span class="si">${</span><span class="nv">VM_CPU_CORES</span><span class="si">}</span> --memory <span class="si">${</span><span class="nv">VM_MEMORY</span><span class="si">}</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --numa <span class="m">1</span> --net0 virtio,bridge<span class="o">=</span>vmbr0 --ostype l26 --agent <span class="m">1</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --pool <span class="si">${</span><span class="nv">VM_RESOURCE_POOL</span><span class="si">}</span> --scsihw virtio-scsi-single
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">qm <span class="nb">set</span> <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> --scsi0 local-lvm:0,import-from<span class="o">=</span><span class="si">${</span><span class="nv">VM_DISK_IMAGE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">qm <span class="nb">set</span> <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> --ide2 local-lvm:cloudinit --boot <span class="nv">order</span><span class="o">=</span>scsi0
</span></span><span class="line"><span class="cl">qm <span class="nb">set</span> <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> --ipconfig0 <span class="nv">ip</span><span class="o">=</span><span class="si">${</span><span class="nv">CLOUD_INIT_IP</span><span class="si">}</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --nameserver <span class="si">${</span><span class="nv">CLOUD_INIT_NAMESERVER</span><span class="si">}</span> --searchdomain <span class="si">${</span><span class="nv">CLOUD_INIT_SEARCHDOMAIN</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">qm <span class="nb">set</span> <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> --ciupgrade <span class="m">1</span> --ciuser <span class="si">${</span><span class="nv">CLOUD_INIT_USER</span><span class="si">}</span> --sshkeys <span class="si">${</span><span class="nv">CLOUD_INIT_SSHKEY</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">qm cloudinit update <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>--agent 1</code> asks for qemu-guest-agent. Alma/Rocky/Fedora ship it or cloud-init installs it (<code>ciupgrade</code>). Amazon Linux 2 and some Ubuntu cloudimgs <strong>will not</strong> show the guest IP in the Proxmox GUI until you install the agent yourself.</p>
<h2 id="4-convert-to-a-template">4. Convert to a template</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">qm <span class="nb">set</span> <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> --name <span class="s2">&#34;</span><span class="si">${</span><span class="nv">VM_NAME</span><span class="si">}</span><span class="s2">-Template&#34;</span>
</span></span><span class="line"><span class="cl">qm template <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Do not start that VM again: clone it.</p>
<h2 id="5-clone-and-boot">5. Clone and 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><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"><span class="nb">export</span> <span class="nv">VM_ID</span><span class="o">=</span><span class="k">$(</span>pvesh get /cluster/nextid<span class="k">)</span>
</span></span><span class="line"><span class="cl">qm clone <span class="si">${</span><span class="nv">TEMPLATE_ID</span><span class="si">}</span> <span class="si">${</span><span class="nv">VM_ID</span><span class="si">}</span> --name <span class="si">${</span><span class="nv">VM_NAME</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">qm start <span class="si">${</span><span class="nv">VM_ID</span><span class="si">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>SSH with the <strong>private</strong> key that matches the <code>.pub</code> you fed cloud-init:</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">ssh user@192.168.0.123 -i ~/.ssh/id_rsa
</span></span></code></pre></td></tr></table>
</div>
</div><p>If Proxmox does not show an IP: serial console, <code>ip neigh</code> on the bridge, or the router’s DHCP leases.</p>
<p>Source: <a href="https://gist.github.com/inetshell/f1d0206d5319c11062845901e4f3d06b">gist inetshell/f1d0206d5319c11062845901e4f3d06b</a>.</p>
]]></content:encoded></item><item><title>Read an HP iLO license key without the web UI</title><link>https://inet.sh/en/posts/ilo/get-license-key/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/ilo/get-license-key/</guid><description>Extract an HP iLO license key from the xmldata?item=CpqKey XML endpoint, with no web-console login.</description><content:encoded><![CDATA[<p>iLO Advanced is licensed by key. If you inherited a ProLiant and the UI will not open (cert, Java, firmware), the BMC still publishes an inventory XML.</p>
<h2 id="the-endpoint">The endpoint</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">curl -sk <span class="s2">&#34;https://&lt;ilo-ip&gt;/xmldata?item=CpqKey&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>In a browser: <code>https://&lt;ilo-ip&gt;/xmldata?item=CpqKey</code>.</p>
<p>The body is XML with the product and the key (or an unlicensed state). This is not Redfish; it is iLO&rsquo;s <code>xmldata</code> interface, the one old HP SIM scripts used.</p>
<h2 id="notes">Notes</h2>
<ul>
<li><code>-k</code> because the iLO certificate is almost never from a public CA.</li>
<li>Some iLOs still serve this over <strong>HTTP</strong> (<code>http://&lt;ilo-ip&gt;/xmldata?item=CpqKey</code>) if HTTPS will not negotiate.</li>
<li>Treat the key as a secret. Do not commit it or paste it.</li>
<li>Reading it does not activate anything: it only <strong>reads</strong> what is already stored on the BMC. Applying a different key is still <code>license</code> in CLP or the UI.</li>
</ul>
<p>See also: <a href="/en/posts/ilo/cli-commands/">iLO CLI commands</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[<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[<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>Upgrade HP iLO firmware over SSH</title><link>https://inet.sh/en/posts/ilo/upgrade-firmware-ssh/</link><pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/ilo/upgrade-firmware-ssh/</guid><description>Upgrade HP iLO firmware over SSH with SMASH CLP: show /map1/firmware1 and load -source http://…</description><content:encoded><![CDATA[<p>When iLO firmware is old enough that browsers refuse its TLS, SSH plus HTTP still works.</p>
<h2 id="1-serve-the-bin-over-http">1. Serve the <code>.bin</code> over HTTP</h2>
<p>iLO <strong>pulls</strong> the firmware; you do not SCP it in. <code>python3 -m http.server</code> on the management network is enough, or nginx. Use the official HPE <code>ilo*.bin</code>.</p>
<h2 id="2-log-in-and-load">2. Log in and load</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">ssh -c aes256-cbc Administrator@&lt;ilo-ip&gt;
</span></span><span class="line"><span class="cl">show /map1/firmware1
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /map1/firmware1
</span></span><span class="line"><span class="cl">load -source http://&lt;http-server&gt;/ilofirmware.bin
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-c aes256-cbc</code> is the iLO 2/3 trick: modern OpenSSH will not offer the BMC&rsquo;s ciphers unless you ask.</p>
<p><code>show /map1/firmware1</code> prints the current version <strong>before</strong> you touch anything. Keep it.</p>
<h2 id="what-to-expect">What to expect</h2>
<ul>
<li><code>load</code> takes minutes. Do not kill the session.</li>
<li>iLO reboots itself. The host <strong>does not</strong> power off (firmware lives on the BMC).</li>
<li>If HTTP is not reachable from the iLO NIC, <code>load</code> fails with a useless error: <code>oemhp_ping</code> the HTTP server.</li>
</ul>
<p>After reboot, <code>show /map1/firmware1</code> again and confirm the version string.</p>
<p>Write-up this is based on: <a href="https://ajmckean.com/upgrade-hp-ilo-via-ssh/">Upgrade HP iLO via SSH</a>.</p>
<p>See also: <a href="/en/posts/ilo/cli-commands/">iLO CLI commands</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[<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[<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>