blog/posts/void-on-zfs.html
2024-06-10 08:47:45 -03:00

849 lines
62 KiB
HTML
Generated
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta name="author" content="Juno Takano" />
<meta name="dcterms.date" content="2024-06-09" />
<title>Void on ZFS • jutty.dev</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
/* The extra [class] is a hack that increases specificity enough to
override a similar rule in reveal.js */
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ color: #cccccc; background-color: #303030; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ffcfaf; } /* Alert */
code span.an { color: #7f9f7f; font-weight: bold; } /* Annotation */
code span.at { } /* Attribute */
code span.bn { color: #dca3a3; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #f0dfaf; } /* ControlFlow */
code span.ch { color: #dca3a3; } /* Char */
code span.cn { color: #dca3a3; font-weight: bold; } /* Constant */
code span.co { color: #7f9f7f; } /* Comment */
code span.cv { color: #7f9f7f; font-weight: bold; } /* CommentVar */
code span.do { color: #7f9f7f; } /* Documentation */
code span.dt { color: #dfdfbf; } /* DataType */
code span.dv { color: #dcdccc; } /* DecVal */
code span.er { color: #c3bf9f; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #c0bed1; } /* Float */
code span.fu { color: #efef8f; } /* Function */
code span.im { } /* Import */
code span.in { color: #7f9f7f; font-weight: bold; } /* Information */
code span.kw { color: #f0dfaf; } /* Keyword */
code span.op { color: #f0efd0; } /* Operator */
code span.ot { color: #efef8f; } /* Other */
code span.pp { color: #ffcfaf; font-weight: bold; } /* Preprocessor */
code span.sc { color: #dca3a3; } /* SpecialChar */
code span.ss { color: #cc9393; } /* SpecialString */
code span.st { color: #cc9393; } /* String */
code span.va { } /* Variable */
code span.vs { color: #cc9393; } /* VerbatimString */
code span.wa { color: #7f9f7f; font-weight: bold; } /* Warning */
</style>
<link rel="stylesheet" href="../assets/css/style.css" />
<link rel="alternate" type="application/rss+xml" title="RSS (English)" href="https://blog.jutty.dev/assets/rss/en.rss"/>
<link rel="alternate" type="application/rss+xml" title="RSS (Português)" href="https://blog.jutty.dev/assets/rss/pt.rss"/>
<link rel="icon" type="image/ico" href="../assets/img/favicon.ico">
<script src="../assets/js/post-l10n.js" defer></script>
</head>
<body>
<header id="title-block-header">
<div id="title-container">
<h1 class="title">Void on ZFS</h1>
</div>
</header>
<main class="article">
<p><img src="../assets/img/posts/void-on-zfs/desk.jpg"
alt="An L-shaped desk with two laptops, an external monitor, a router and a third headless computer in a tower case with several power cables connected to a power strip on top of it. Next to the power strip are two cellphones, a long red box, and a charging case for Bluetooth headphones with a red LED on. The tower case has three stickers on it: one with the machine specifications, one the FreeBSD logo, and one reading “platform feodalism (sic) is so 1492”. Scattered around the machines are some office supplies, medicine containers, a screwdriver, a notepad, a small notebook, an NVMe SSD card on top of its packaging, a mug, a water bottle and a scarf. Hanging on the wall are a small painting of a dead tree before the twilight, prayer beads, a sunflower-pattern keychain and a calendar. Between the desk and the camera, a green plastic chair has a pillow and blanket on top of it. A few wires and cardboard boxes are visible under the desk." /></p>
<p>June is here. It brings the usual cold weather and some extra
rhinitis complications. With that I find myself in a recovery mood
Sunday, wrapped in a blanket with a mug of tea, a screwdriver, some
notes on paper, a flash drive, a couple of NVMe cards and the trio of
Unix-powered machines that will help me get this done.</p>
<p>The mission is to get a root-on-ZFS EFI installation of Void Linux
with ZFSBootMenu on a Dell Latitude 7480.</p>
<p>To my left, a ceiling-collapse-survivor Sony VAIO is running NetBSD
with spectrwm. Its split with sakura and tmux on a terminal to one
side, where Neovim is storing these words, and Firefox on the other,
ready to fetch all the docs. In the middle, the object of todays
operation. And to my right, a headless PC board runs FreeBSD with ZFS,
holding all the backups needed for the post-install tasks.</p>
<p><img src="../assets/img/posts/void-on-zfs/duo.jpg"
alt="Two laptops side-by-side on a desk, each with a USB keyboard plugged in. Writing utensils inside a holder and post-its are between the two. A sunflower-patterned keychain and prayer beads hang from the wall. The computer on the left has a brown cloth for a wrist rest in front of its USB keyboard, to the left of which lies a small blue Campus notebook." /></p>
<p>This lengthy post, written not after the fact but during it, is my
way of documenting and also sharing how it all went. Additionally, its
a way to delve deeper into many of the things the ZFSBootMenu docs leave
unexplained, an urge I already had yesterday as I just tried it out
without much modification.</p>
<p>Last night, I ran through the <a
href="https://docs.zfsbootmenu.org/en/latest/guides/void-linux/uefi.html">ZFSBootMenu
documentation guide for Void</a> and followed it both on a VM and then
on an external SATA HDD plugged through a USB case, taking some notes
and getting a general idea of the process.</p>
<p>The Void installer does not support ZFS out of the box, so the <a
href="https://docs.voidlinux.org/">Void Handbook</a> itself recommends
the ZFSBootMenu documentation before <a
href="https://docs.voidlinux.org/installation/guides/zfs.html">its
own</a> (a manual chroot installation) when it comes to doing a
ZFS-on-root install. This guide from ZFSBootMenu is what well be
following throughout this post.</p>
<p>Do note that, while comprehensive, my account is no replacement for
<a
href="https://docs.zfsbootmenu.org/en/latest/guides/void-linux/uefi.html">the
original guide</a>. Although more concise, it contains certain notes not
included in this post and covers a larger set of possibilities than I
did here. Some of the code blocks youll see here are identical to the
ones from the guide, but many others are specific to how I did things,
so keep that in mind and try things before going with your final
installation.</p>
<h2 id="why-void">Why Void?</h2>
<p>I dont really enjoy distro-hopping. I usually will spend a few years
on the same OS and only switch for good reason and after some thorough
testing. And after some Debian time, I felt interested in trying Void
for a few reasons:</p>
<ul>
<li>rolling, but stable</li>
<li>runit init system</li>
<li>BSD-like rc files</li>
<li>BSD-like handbook documentation</li>
<li>numerous, up to date, but stable packages</li>
</ul>
<p>After trying it, some other features made me settle:</p>
<ul>
<li>fast and feature-packed package manager</li>
<li>very fast startup time (kudos to runit)</li>
<li>first-class support in ZFSBootMenu</li>
</ul>
<p>The Void package manager, <a
href="https://docs.voidlinux.org/xbps/index.html">xbps</a>, has several
interesting features. One of my favorites, for a taste, is
<code>xbps-query --cat</code>, which shows the original contents of a
given file in a package.</p>
<p>For example,
<code>xpbps-query --cat /etc/zfsbootmenu/config.yaml zfsbootmenu</code>
will show you the original content of the <code>config.yaml</code> file
in the <code>zfsbootmenu</code> package. You can use it for very core
packages like <code>base-system</code> or <code>runit-void</code> to
determine the original version of files shipped by them.</p>
<h2 id="and-why-zfs">And why ZFS?</h2>
<p>My first contact with ZFS was when using FreeBSD, which provides it
as an option in its installer, making it a bit too easy not to try.
Having a server on ZFS means all the data it holds can be safeguarded
and transferred in robust ways, and mistakes are also easier to recover
from.</p>
<p>Aside from all the data integrity features and flexibility it brings,
the features that interest me the most are the ones for managing
snapshots.</p>
<p>ZFS snapshots allow you to store the filesystem state at a given
point in time, and to compare against, access the content of, and fully
revert to this state. After the guide has been followed throughout, an
extra section at the end of this post has some snapshot basics.</p>
<h2 id="getting-in">Getting in</h2>
<p>So, first things first, open the machine up and swap the NVMe cards.
For me, that means getting my 128 GB NVMe stick, which I use basically
for tests, and replace it with the 256 GB one which currently has Debian
on it. Yes, I get by just fine with that much.</p>
<p>While a bit dusty, the machine was overall in good state. The release
date for the model is 2017, which for my computing standards is very
recent.</p>
<p>It has a single NVMe slot, one 16 GB RAM stick and one unused RAM
slot. If you look closely, you can notice a dent on the vent tube
connecting the cooler to the CPU. Despite this, it very rarely heats
up.</p>
<p><img src="../assets/img/posts/void-on-zfs/karu-inside.jpg"
alt="The Dell laptop seen from above, lid closed, with the screen against the desk and the bottom cover removed, exposing the motherboard." /></p>
<p>Next up is to boot up <a
href="https://github.com/leahneukirchen/hrmpf">hrmpf</a> in EFI
mode.</p>
<p>hrmpf is a Void-based rescue system maintained by a Void team member
and distributed as a bootable image that can accomplish many things,
some of them being a full Void installation, entering a proper chroot,
and being ZFS-ready with the needed drivers and tools.</p>
<p>Once booted into it, EFI support can be confirmed by filtering the
output of <code>dmesg</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">dmesg</span> <span class="kw">|</span> <span class="fu">grep</span> <span class="at">-i</span> efivars</span></code></pre></div>
<p>The output should contain “Registered efivars operations”.</p>
<p>Make sure you have an Internet connection at this point. Most of the
following steps will run fine without one, but closer to the end, when
installing the Void base system, it will all go to waste if we cant
reach a package mirror.</p>
<h2 id="setting-up-the-installation-environment">Setting up the
installation environment</h2>
<p>The ZFSBootMenu guide uses some variables in order to avoid mistakes
and make the instructions more portable across the different storage
types and supported operating systems.</p>
<h3 id="etcos-release"><code>/etc/os-release</code></h3>
<p>The <code>/etc/os-release</code> file typically contains information
on the operating system you are running.</p>
<p>In the hrmpf live system, these are its contents:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="va">NAME</span><span class="op">=</span><span class="st">&quot;Void&quot;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="va">ID</span><span class="op">=</span><span class="st">&quot;void&quot;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="va">PRETTY_NAME</span><span class="op">=</span><span class="st">&quot;Void Linux&quot;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="va">HOME_URL</span><span class="op">=</span><span class="st">&quot;https://voidlinux.org/&quot;</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="va">DOCUMENTATION</span><span class="op">=</span><span class="st">&quot;https://docs.voidlinux.org/&quot;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="va">LOGO</span><span class="op">=</span><span class="st">&quot;void-logo&quot;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="va">ANSI_COLOR</span><span class="op">=</span><span class="st">&quot;0;38;2;71;128;97&quot;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="va">DISTRIB_ID</span><span class="op">=</span><span class="st">&quot;void</span></span></code></pre></div>
<p>For comparison, here is FreeBSDs <code>os-release</code> file:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="va">NAME</span><span class="op">=</span>FreeBSD</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="va">VERSION</span><span class="op">=</span><span class="st">&quot;14.0-RELEASE&quot;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="va">VERSION_ID</span><span class="op">=</span><span class="st">&quot;14.0&quot;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="va">ID</span><span class="op">=</span>freebsd</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="va">ANSI_COLOR</span><span class="op">=</span><span class="st">&quot;0;31&quot;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="va">PRETTY_NAME</span><span class="op">=</span><span class="st">&quot;FreeBSD 14.0-RELEASE&quot;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="va">CPE_NAME</span><span class="op">=</span><span class="st">&quot;cpe:/o:freebsd:freebsd:14.0&quot;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="va">HOME_URL</span><span class="op">=</span><span class="st">&quot;https://FreeBSD.org/&quot;</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="va">BUG_REPORT_URL</span><span class="op">=</span><span class="st">&quot;https://bugs.FreeBSD.org/&quot;</span></span></code></pre></div>
<p>In contrast, NetBSD has no such file.</p>
<p>For the purposes of the ZFSBootMenu guide, only the <code>$ID</code>
value appears to be used. And because the file already is structured as
shell-compatible variable assignments, we just source it:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="bu">source</span> /etc/os-release</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">ID</span></span></code></pre></div>
<h3 id="hostid"><code>hostid</code></h3>
<p>Required by ZFS intallations, a host ID is a 32-bit hexadecimal value
that, supposedly, will uniquely identify a machine. Considering the
number of existing machines and the 32-bit range, you might guess why I
say <em>supposedly</em>.</p>
<p>If your machine has the <code>hostid</code> utilities, you can see
the host ID by simply running <code>hostid</code>. Prior to generation,
my hrmpf live system reports <code>00000000</code>.</p>
<p>It cant provide a real guarantee that it will be unique, so its up
to you to take care that it is unique among <em>your</em> machines. Read
on for why thats hardly an issue.</p>
<p>From the <code>gethostid(3)</code> man page:</p>
<blockquote>
<p>[…] a unique 32-bit identifier for the current machine. The 32-bit
identifier was intended to be unique among all UNIX systems in
existence. This normally resembles the Internet address for the local
machine, as returned by <code>gethostbyname(3)</code>, and thus usually
never needs to be set.</p>
</blockquote>
<p>This seems to be more or less a legacy feature. In Voids man page
for <code>gethostid(3)</code>, you see this in the history section:</p>
<blockquote>
<p>4.2BSD; dropped in 4.4BSD. SVr4 and POSIX.1-2001 include gethostid()
but not sethostid().</p>
</blockquote>
<p>Still, it is something that OpenZFS requires to be set:</p>
<blockquote>
<p>At time of import or creation, the pool stores the systems unique
host ID and for the purposes of supporting multipath, import into other
systems will fail unless forced. <br/><br/><a
href="https://openzfs.readthedocs.io/en/latest/introduction.html">OpenZFS
docs, Introduction to ZFS: Storage pools</a></p>
</blockquote>
<p><code>zgenhostid</code>, which is shipped by OpenZFS, according to
its man page “emulates the <code>genhostid(1)</code> utility and is
provided for use on systems which do not include the utility or do not
provide the <code>sethostid(3)</code> function.”</p>
<p>When used without arguments, these commands will generate a random
host ID. But they can also be passed a hexadecimal value, which gets
stored by default in <code>/etc/hostid</code> unless another path is
given with <code>-o</code>.</p>
<p>Considering this information, it threw me off a bit that the
ZFSBootMenu guide tells you to specify an arbitrary host ID rather than
generate a random one:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zgenhostid</span> <span class="at">-f</span> 0x00bab10c</span></code></pre></div>
<p>If they must be unique, that seems odd.</p>
<p>The value <code>0x00bab10c</code> actually has significance in the
context of OpenZFS as an identifier (and leetspeak) for its uberblock.
However, it apparently is totally unrelated to host IDs.</p>
<p>Should you be curious still, you can refer to <a
href="https://github.com/zbm-dev/zfsbootmenu/discussions/465">this
GitHub discussion</a> where a ZFSBootMenu user brought this exact
question to the developers.</p>
<p>According to the answer given above, the uniqueness of host IDs is
useful for “multipathed SAS enclosures with two discrete head unis
attached”, which is an enterprise-grade storage solution.</p>
<p>The value <code>0x00bab10c</code> is indeed unrelated and chosen for
easy identification. Any value may be used, but when using the pre-built
ZFSBootMenu images it may make the process slightly slower (around
250ms) as ZFSBootMenu will have to “discover the hostid every boot”.</p>
<h3 id="disk-variables">Disk variables</h3>
<p>Here too, the ZFSBootMenu guide <a
href="https://docs.zfsbootmenu.org/en/latest/guides/void-linux/uefi.html#define-disk-variables">works
with a set of variables</a> to make it easier covering different
possible storage types:</p>
<ul>
<li><code>BOOT_DISK</code>, <code>BOOT_PART</code> and
<code>BOOT_DEVICE</code></li>
<li><code>POOL_DISK</code>, <code>POOL_PART</code> and
<code>POOL_DEVICE</code></li>
</ul>
<p>My target device is an NVMe at <code>nvme0n1</code>, so Ill
have:</p>
<ul>
<li><code>BOOT_DISK="/dev/nvme0n1"</code></li>
<li><code>BOOT_PART="1"</code></li>
<li><code>BOOT_DEVICE="${BOOT_DISK}p${BOOT_PART}"</code>
<ul>
<li>which evaluates to <code>/dev/nvme0n1p1</code></li>
</ul></li>
<li><code>POOL_DISK="/dev/nvme0n1"</code></li>
<li><code>POOL_PART="2"</code></li>
<li><code>POOL_DEVICE="${POOL_DISK}p${POOL_PART}"</code>
<ul>
<li>which evaluates to <code>/dev/nvme0n1p2</code></li>
</ul></li>
</ul>
<p>While this may seem silly at first, it allows using the values
separately in the next steps. It also makes the docs a lot more concise
while covering several possible disk setups.</p>
<h3 id="confirming-the-environment-setup">Confirming the environment
setup</h3>
<p>At this point, we should be able to print something like this in our
environment:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co"># env | grep ID</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="va">ID</span><span class="op">=</span>void</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co"># hostid</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="ex">00bab10c</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="co"># echo $BOOT_DEVICE</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="ex">/dev/nvme0n1p1</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="co"># echo $POOL_DEVICE</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="ex">/dev/nvme0n1p2</span></span></code></pre></div>
<p>Take care to keep this same environment for all the next steps as
they depend on it. For instance, the hrmpf live system ships tmux. While
that is great and I have used it throughout, you must be careful to use
a single pane for all the actual steps, and the other panes just for
secondary things like looking up man pages or checking file
contents.</p>
<h2 id="filesystem-setup">Filesystem setup</h2>
<h3 id="wiping">Wiping</h3>
<p>The first step is to clear the current ZFS label information from the
device:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zpool</span> labelclear <span class="at">-f</span> <span class="st">&quot;</span><span class="va">$POOL_DISK</span><span class="st">&quot;</span></span></code></pre></div>
<p>The <code>-f</code> option will “treat exported or foreign devices as
inactive”, per the man page.</p>
<p>This step fails consistenly for me, which I assume is because the
previous filesystem was not ZFS to begin with.</p>
<p>Next, we will use <code>wipefs</code> to erase the current filesystem
signature.</p>
<p>This command is not ZFS-specific, but part of the kernel utilities.
It does not erase the filesystems themselves, nor their content, but the
signatures that aid in their detection.</p>
<p>Without any options, it will list all the filesystems that are still
visible:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># wipefs &quot;$BOOT_DISK&quot;</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ex">DEVICE</span> OFFSET TYPE UUID LABEL</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="ex">nvme0n1</span> 0x200 gpt</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="ex">nvme0n1</span> 0x3d9e655e00 gpt</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="ex">nvme0n1</span> 0x1fe PMBR</span></code></pre></div>
<p>The <code>-a</code> option is for erasing all signatures. This means
it will “scan the device again after each modification until no magic
string [signature] is found”, as per its man page.</p>
<p>In my case:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ex">wipefs</span> <span class="at">-a</span> <span class="st">&quot;</span><span class="va">$POOL_DISK</span><span class="st">&quot;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="ex">wipefs</span> <span class="at">-a</span> <span class="st">&quot;</span><span class="va">$BOOT_DISK</span><span class="st">&quot;</span></span></code></pre></div>
<p>Along the guide, commands are sometimes repeated for both
<code>$POOL_DISK</code> and <code>$BOOT_DISK</code>. If you are using
the same disk for both, this may be redundant, although also
harmless.</p>
<p>This is my case, so I am not typically running it twice. Ill still
leave it as is however, so as not to mislead the reader.</p>
<p>Now, when listing the signatures again with
<code>wipefs "$BOOT_DISK"</code>, there should be no output.</p>
<p>Finally, the current MBR and GPT tables must be destroyed. For this,
the ZFSBootMenu guide uses <code>sgdisk</code>. This is also not
ZFS-specific.</p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">sgdisk</span> <span class="at">--zap-all</span> <span class="st">&quot;</span><span class="va">$POOL_DISK</span><span class="st">&quot;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="ex">sgdisk</span> <span class="at">--zap-all</span> <span class="st">&quot;</span><span class="va">$BOOT_DISK</span><span class="st">&quot;</span></span></code></pre></div>
<p>The <code>--zap-all</code> option contrasts with <code>--zap</code>
in that it will destroy both MBR and GPT partition tables.</p>
<h3 id="partitioning">Partitioning</h3>
<p>In the ZFSBootMenu guide, <code>sgdisk</code> is used again for
creating the partitions:</p>
<div class="sourceCode" id="cb11"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="ex">sgdisk</span> <span class="dt">\</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-n</span> <span class="st">&quot;</span><span class="va">${BOOT_PART}</span><span class="st">:1m:+512m&quot;</span> <span class="dt">\</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="at">-t</span> <span class="st">&quot;</span><span class="va">${BOOT_PART}</span><span class="st">:ef00&quot;</span> <span class="st">&quot;</span><span class="va">$BOOT_DISK</span><span class="st">&quot;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="ex">sgdisk</span> <span class="dt">\</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="at">-n</span> <span class="st">&quot;</span><span class="va">${POOL_PART}</span><span class="st">:0:-10m&quot;</span> <span class="dt">\</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="at">-t</span> <span class="st">&quot;</span><span class="va">${POOL_PART}</span><span class="st">:bf00&quot;</span> <span class="st">&quot;</span><span class="va">$POOL_DISK</span><span class="st">&quot;</span></span></code></pre></div>
<p>In the commands above, option <code>-n</code> is short for
<code>--new</code>, and is specifying the start and end sectors by using
relative kibibyte measures. The format is
<code>--new partnum:start:end</code>.</p>
<p>Breaking it down:</p>
<ul>
<li><code>1m</code> 1 mebibyte from the start of the disk</li>
<li><code>+512m</code> 512 mebibytes after the default start sector</li>
<li><code>-10m</code> 10 mebibytes before the last available sector</li>
<li><code>0</code> the default value</li>
</ul>
<p>In the list above, “default” is “the start of the largest available
block for the start sector and the end of the same block for the end
sector”, as per the <code>sgdisk</code> man page.</p>
<p><code>1:1m:+512m</code>, therefore, means that partition 1 will start
1 mebibyte from the start of the disk and end 512 mebibytes after the
start of the largest available block.</p>
<p><code>2:0:-10m</code>, in turn, means partition 2 will begin at the
start of the largest available block and end 10 mebibytes before the
last available sector.</p>
<p>Option <code>-t</code> is for setting the typecode for each
partition. Typecode <code>ef00</code> is for the EFI system partition,
and typecode <code>bf00</code> is for “Solaris root”, the Unix system
upon whose ZFS implementation OpenZFS was based.</p>
<p>For a list of typecodes, see <code>sgdisk -L</code>.</p>
<p>While just running these commands as-is is your safest option, you
might have a different layout in mind or prefer an interactive UI.</p>
<p>For one thing, Ive had issues in the past with the boot partition
being too small, so Ill be using <code>2g</code> instead of
<code>512m</code> for it.</p>
<p><code>sgdisk</code> has a friendlier counterpart named
<code>gdisk</code>, which you can use just by passing it the disk path,
as in <code>gdisk /dev/sda</code>.</p>
<p>At this point, you should be safe to try partitioning and going back
to wiping as needed until you are satisfied.</p>
<p>When you are done, you can use <code>lsblk</code> to confirm the
results. The following will show you the options just configured:</p>
<div class="sourceCode" id="cb12"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ex">lsblk</span> <span class="at">-o</span> NAME,SIZE,TYPE,PARTTYPENAME</span></code></pre></div>
<h3 id="creating-the-pool">Creating the pool</h3>
<p>This part of the guide was the one that actually made me want to
delve deeper and understand what each option meant.</p>
<p>With little knowledge about ZFS still, I wanted to understand
precisely what was happening here, but also what a pool even is and what
its creation meant.</p>
<p>Heres the <code>zpool(8)</code> man page:</p>
<blockquote>
<p>A storage pool is a collection of devices that provides physical
storage and data replication for ZFS datasets. All datasets within a
storage pool share the same space.</p>
</blockquote>
<p>The definition of a dataset is then indicated to be at
<code>zfs(8)</code>:</p>
<blockquote>
<p>A dataset is identified by a unique path within the ZFS namespace:
<br/> <code>pool[/component]/component</code> for example:
<code>rpool/var/log</code></p>
</blockquote>
<p>Here, its also explained that a dataset can be a file system,
logical volume, snapshot or bookmark.</p>
<p>Further information is also hinted to be found at
<code>zpoolconcepts(7)</code>.</p>
<p>At this point you start to notice the breadth of knowledge available
in the documentation. The man pages are not only comprehensible, but
sometimes contain several examples on how to apply their concepts. Each
command has their own man page named with a hyphen for separation, as in
<code>zpool-create</code>.</p>
<p>Well be exploring only the <code>zpool-create(8)</code> command in
depth, in particular the options used in the ZFSBootMenu guide:</p>
<ul>
<li><code>-f</code> force the use of virtual devices, even if they
appear in use</li>
<li><code>-o feature=value</code> set a pool feature</li>
<li><code>-O property=value</code> set a file system property in the
root file system of the pool</li>
<li><code>-o compatibility=off|legacy|file[,file]</code> specify a
compatibility feature set</li>
<li><code>-m mountpoint</code> the mountpoint (default:
<code>/pool</code>)</li>
<li><code>pool</code> the pool</li>
<li><code>vdev</code> the virtual device</li>
</ul>
<p>The listing with pool features (including compatibility feature sets)
is at <code>zpool-features(7)</code>. Pool properties are at
<code>zpoolprops(7)</code> and file system properties at
<code>zfsprops(7)</code>.</p>
<p>In the guide, these are the options given:</p>
<div class="sourceCode" id="cb13"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zpool</span> create <span class="at">-f</span> <span class="dt">\</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-o</span> ashift=12 <span class="dt">\</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="at">-O</span> compression=lz4 <span class="dt">\</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> <span class="at">-O</span> acltype=posixacl <span class="dt">\</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a> <span class="at">-O</span> xattr=sa <span class="dt">\</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> <span class="at">-O</span> relatime=on <span class="dt">\</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> <span class="at">-o</span> autotrim=on <span class="dt">\</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> <span class="at">-o</span> compatibility=openzfs-2.1-linux <span class="dt">\</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a> <span class="at">-m</span> none zroot <span class="st">&quot;</span><span class="va">$POOL_DEVICE</span><span class="st">&quot;</span></span></code></pre></div>
<p>Among the options above, several pool features and system properties
are set:</p>
<ul>
<li><code>-o ashift=12</code> “Alignment shift”, used to calculate
physical sector sizes.This is discussed at greater length in the <a
href="https://openzfs.github.io/openzfs-docs/Performance%20and%20Tuning/Workload%20Tuning.html#alignment-shift-ashift">online
documentation on Workload Tuning</a></li>
<li><code>-O compression=lz4</code> Sets the compression algorithm used
(<a
href="https://en.wikipedia.org/wiki/LZ4_(compression_algorithm)">LZ4</a>)</li>
<li><code>-O acltype=posixacl</code> Whether <a
href="https://en.wikipedia.org/wiki/Access-control_list">ACLs</a> are
enabled and what type to use. The value <code>posixacl</code> is
equivalent to <code>posix</code> (default on Linux: off)</li>
<li><code>-O xattr=sa</code> Enables extended attributes. If value is
<code>on</code>, uses directory-based extended attributes, while
<code>sa</code> uses system-attribute-based. The latter has performance
benefits, and is important for ACLs and SELinux usage</li>
<li><code>-O relatime=on</code> “Causes the access time to be updated
relative to the modify or change time.” Also, “access time is only
updated if the previous access time was earlier than the current modify
or change time or if the existing access time hasnt been updated within
the past 24hours”</li>
<li><code>-o autotrim=on</code> Automatically reclaims unused blocks
from time to time. Can put the filesystem under some stress</li>
</ul>
<p>The last option, the compatibility feature set, specifies in this
case a relative filename to
<code>/usr/share/zfs/compatibility.d</code>:</p>
<ul>
<li><code>-o compatibility=openzfs-2.1-linux</code></li>
</ul>
<p><code>zpool-create(8)</code> also states:</p>
<blockquote>
<p>By default, all supported features are enabled on the new pool. The
<code>-d</code> option and the <code>-o</code> compatibility property
[…] can be used to restrict the features that are enabled, so that the
pool can be imported on other releases of ZFS.</p>
</blockquote>
<p>The compatibility option <code>openzfs-2.1-linux</code> is described
as a “conservative” choice in the ZFSBootMenu guide and in my tests had
little impact, so I decided to not use it for this installation.</p>
<h3 id="creating-the-filesystems">Creating the filesystems</h3>
<p>Once the pool is ready, the filesystems can be created.</p>
<p>For this task, the <code>zfs</code> command is used with
<code>create</code>:</p>
<div class="sourceCode" id="cb14"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> create <span class="at">-o</span> mountpoint=none zroot/ROOT</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> create <span class="at">-o</span> mountpoint=/ <span class="at">-o</span> canmount=noauto zroot/ROOT/<span class="va">${ID}</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> create <span class="at">-o</span> mountpoint=/home zroot/home</span></code></pre></div>
<p>The ZFSBootMenu guide explains at this point that if
<code>canmount=noauto</code> is not set on file systems with the
<code>/</code> mountpoint, the OS will try to mount them all and fail.
It goes on to say:</p>
<blockquote>
<p>Automatic mounting of / is not required because the root file system
is explicitly mounted in the boot process.</p>
</blockquote>
<p>After the filesystems have been created, the boot file system must be
set.</p>
<div class="sourceCode" id="cb15"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zpool</span> set bootfs=zroot/ROOT/<span class="va">${ID}</span> zroot</span></code></pre></div>
<p>Essentially, this is saying “set <code>zroot</code>s
<code>bootfs</code> property to <code>zroot/ROOT/void</code></p>
<h3 id="export-reimport-and-mount">Export, reimport and mount</h3>
<p>The next steps consist in exporting and then importing the pool with
a given mountpoint.</p>
<div class="sourceCode" id="cb16"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zpool</span> export zroot</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="ex">zpool</span> import <span class="at">-N</span> <span class="at">-R</span> /mnt zroot</span></code></pre></div>
<p>From what I gather, exporting means putting the pool in a more
portable state. According to the <code>zpool-export(8)</code> man page,
“the devices [marked as exported] can be moved between systems […] and
imported as long as a sufficient number of devices are present.”</p>
<p>If <code>zfs import</code> is used without any arguments, it will
list the exported pools available to be imported.</p>
<p>The <code>-N root</code> option imports the pool without mounting any
of its file systems, the <code>-R</code> option “sets the
<code>cachefile</code> property to <code>none</code> and the
<code>altroot</code> property to <code>root</code>”. In this case, that
<code>root</code> will be <code>/mnt</code>.</p>
<p><code>altroot</code> stands for the alternate root directory. In
<code>zpoolprops(7)</code>, this becomes clearer when it is stated that
“this directory is prepended to any mount points within the pool.”</p>
<p>Once re-imported, we can mount:</p>
<div class="sourceCode" id="cb17"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> mount zroot/ROOT/<span class="va">${ID}</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> mount zroot/home</span></code></pre></div>
<p>And verify that all is mounted correctly with
<code>mount | grep mnt</code>:</p>
<div class="sourceCode" id="cb18"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="co"># mount | grep mnt</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="ex">zroot/ROOT/void</span> on /mnt type zfs <span class="er">(</span><span class="ex">rw,relatime,xattr,posixacl,casesensitive</span><span class="kw">)</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="ex">zroot/home</span> on /mnt/home type zfs <span class="er">(</span><span class="ex">rw,relatime,xattr,posixacl,casesensitive</span><span class="kw">)</span></span></code></pre></div>
<p>Lastly, we request the device events from the kernel to update the
device symlinks:</p>
<div class="sourceCode" id="cb19"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="ex">udevadm</span> trigger</span></code></pre></div>
<h2 id="setting-up-void">Setting up Void</h2>
<h3 id="installation">Installation</h3>
<p>So far, not much here was Void-specific. This is when we start
bootstrapping the void system into the filesystem we laid out.</p>
<div class="sourceCode" id="cb20"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="va">XBPS_ARCH</span><span class="op">=</span>x86_64 <span class="ex">xbps-install</span> <span class="dt">\</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-S</span> <span class="at">-R</span> https://repo-fastly.voidlinux.org/current <span class="dt">\</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> <span class="at">-r</span> /mnt base-system</span></code></pre></div>
<p>Here, we are passing an environment variable to set the architecture
to <code>x86_64</code>, then use <code>xbps-install</code> from the xbps
package manager to fetch the Void base system.</p>
<p><code>-S</code> takes care of synchronizing the data from the mirror
so that package data is fetched, <code>-R</code> allows us to manually
specify the repository for this run, and <code>-r</code> allows choosing
a different root directory to act upon.</p>
<p>Here, I chose the Fastly mirror over the ServerCentral one. <a
href="https://xmirror.voidlinux.org/">Any working mirror</a> should
do.</p>
<p>Note that not all mirrors have the same content at the root of their
URL. Some point directly to a Void repo, some dont. You can access the
mirror in a browser or otherwise inspect it to find the path to the
<code>current</code> directory.</p>
<p>With this done, we can copy the host ID file, which will also be
required in our final system, and we are ready to chroot.</p>
<div class="sourceCode" id="cb21"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="fu">cp</span> /etc/hostid /mnt/etc</span></code></pre></div>
<h3 id="chrooting">chrooting</h3>
<p>We will chroot into the system mounted at the <code>/mnt</code>
directory using <code>xchroot</code>, which is part of the xbps
<code>xtools</code> package and should already be available on hrmpf. It
provides a <a
href="https://docs.voidlinux.org/config/containers-and-vms/chroot.html#chroot-usage">more
sane</a> chroot than the plain one, in particular regarding the required
mountpoints:</p>
<div class="sourceCode" id="cb22"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="ex">xchroot</span> /mnt</span></code></pre></div>
<p>This is a good time to get back to the notes I mentioned taking the
day before.</p>
<p><img src="../assets/img/posts/void-on-zfs/notes.jpg"
alt="A block of paper with some notes scribbled: “check connection first of all”, “reconfigure after chroot”, “see /usr/share/doc/efibootmgr/README.voidlinux for automatic EFI entry management”, “superb docs”, “take the first snapshot ASAP”. An arrow points from the last note to “on chroot?” Visible above the block of paper is a keyboard. To the right, the tip of a notebook and a piece of brown cloth are visible. On top of the block, there is a mechanical pencil and a Tombow MONO One plastic eraser." /></p>
<h4 id="reconfiguring-packages">Reconfiguring packages</h4>
<p>After chrooting, it may be a good idea to run
<code>xbps-reconfigure</code> to make sure packages are properly
configured. This is because in the bootstrap process some packets may
have tried to configure themselves while relying on directories that
were not mounted anywhere.</p>
<p>This is particularly true for <a
href="https://github.com/OSInside/kiwi/issues/1867"><code>dracut</code></a>,
which is a tool that generates initramfs and initrd images, therefore
being critical to the early boot process. You might see error messages
related to it in your first run of xbps outside of the chroot, when
installing the base system.</p>
<p>To reconfigure <strong>all</strong> packages, just run
<code>xbps-reconfigure -fa</code>. If youd rather only reconfigure
<code>dracut</code>, go with
<code>xpbs-reconfigure -f dracut</code>.</p>
<h4 id="root-password">root password</h4>
<p>As early as possible is a good time to run <code>passwd</code> and
set the root password.</p>
<h4 id="rc.conf"><code>rc.conf</code></h4>
<p><code>runit</code> reads the <code>/etc/rc.conf</code> file during
startup to configure the system, setting up things like the keymap,
hardware clock and terminal font.</p>
<p>For your reference, here is what I added to mine during the
installation:</p>
<div class="sourceCode" id="cb23"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="va">HARDWARECLOCK</span><span class="op">=</span><span class="st">&quot;UTC&quot;</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="va">KEYMAP</span><span class="op">=</span><span class="st">&quot;br-abnt2&quot;</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="va">FONT</span><span class="op">=</span><span class="st">&quot;ter-120n&quot;</span></span></code></pre></div>
<h4 id="time-zone-and-locale">Time zone and locale</h4>
<p>To configure your local time zone, create a symlink at
<code>/etc/localtime</code> that points to the corresponding time zone
in the <code>/usr/share/zoneinfo</code> directory.</p>
<div class="sourceCode" id="cb24"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ln</span> <span class="at">-sf</span> /usr/share/zoneinfo/<span class="op">&lt;</span>timezone<span class="op">&gt;</span> /etc/localtime</span></code></pre></div>
<p>Unless you are using <code>musl</code>, you also want to set and
generate the <code>glibc</code> locales. Edit
<code>/etc/default/libc-locales</code> and uncomment the desired
locales, then run <code>xbps-reconfigure -f glibc-locales</code>.</p>
<h4 id="dracut">dracut</h4>
<p><code>dracut</code> generates file system images used by the kernel
at the very early stages of boot. We have to make it able to identify
our ZFS root filesystem by enabling the proper modules. This is
accomplished by creating <code>/etc/dracut.conf.d/zol.conf</code>
with:</p>
<div class="sourceCode" id="cb25"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="va">nofsck</span><span class="op">=</span><span class="st">&quot;yes&quot;</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="va">add_dracutmodules</span><span class="op">+=</span><span class="st">&quot; zfs &quot;</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="va">omit_dracutmodules</span><span class="op">+=</span><span class="st">&quot; btrfs &quot;</span></span></code></pre></div>
<p>Notice the spaces surrounding the module names.</p>
<h2 id="installing-and-configuring-zfsbootmenu">Installing and
configuring ZFSBootMenu</h2>
<p>We are now ready to install both ZFS and ZFSBootMenu. Lets start
with ZFS:</p>
<div class="sourceCode" id="cb26"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="ex">xbps-install</span> <span class="at">-R</span> https://repo-fastly.voidlinux.org/current zfs</span></code></pre></div>
<p>Now, before installing ZFSBootMenu, we set the kernel commandline.
This is the command line that will be used by the Linux kernel, so any
options you are used to go here.</p>
<p>The ZFSBootMenu guide has only the <code>quiet</code> option. In my
case, I added <code>net.ifnames=0</code> to have the classic
<code>eth0</code>, <code>wlan0</code> network interface names, and
<code>fbcon=nodefer video=efifb:nobgrt</code>, which prevents the
manufacturers logo from showing after boot and sometimes obscuring the
boot process output.</p>
<div class="sourceCode" id="cb27"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> set org.zfsbootmenu:commandline=<span class="st">&quot;quiet net.ifnames=0 fbcon=nodefer video=efifb:nobgrt&quot;</span> zroot/ROOT</span></code></pre></div>
<p>We also need a <code>vfat</code> filesystem on our boot device:</p>
<div class="sourceCode" id="cb28"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="ex">mkfs.vfat</span> <span class="at">-F32</span> <span class="st">&quot;</span><span class="va">$BOOT_DEVICE</span><span class="st">&quot;</span></span></code></pre></div>
<p>Now we can add an <code>/etc/fstab</code> entry pointing to it, and
mount:</p>
<div class="sourceCode" id="cb29"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$(</span><span class="ex">blkid</span> <span class="kw">|</span> <span class="fu">grep</span> <span class="st">&quot;</span><span class="va">$BOOT_DEVICE</span><span class="st">&quot;</span> <span class="kw">|</span> <span class="fu">cut</span> <span class="at">-d</span> <span class="st">&#39; &#39;</span> <span class="at">-f</span> 2<span class="va">)</span><span class="st"> /boot/efi vfat defaults 0 0&quot;</span> <span class="op">&gt;&gt;</span> /etc/fstab</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> /boot/efi</span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="fu">mount</span> /boot/efi</span></code></pre></div>
<p>Into this directory we just mounted, we can now install
ZFSBootMenu.</p>
<p>The guide provides two different paths here: a prebuilt image or the
Void package, which you can get through xbps.</p>
<p>While there are advantages to both, I decided to go with the prebuilt
image since Id rather the package manager not touch the boot manager on
updating. This has the downside of you having to take care of being
aware of any relevant versions and when to upgrade to them.</p>
<div class="sourceCode" id="cb30"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="ex">xbps-install</span> curl</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> /boot/efi/EFI/ZBM</span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-o</span> /boot/efi/EFI/ZBM/VMLINUZ.EFI <span class="at">-L</span> https://get.zfsbootmenu.org/efi</span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a><span class="fu">cp</span> /boot/efi/EFI/ZBM/VMLINUZ.EFI /boot/efi/EFI/ZBM/VMLINUZ-BACKUP.EFI</span></code></pre></div>
<p>If youd rather use the repository package, see the <a
href="https://docs.zfsbootmenu.org/en/latest/guides/void-linux/uefi.html#install-zfsbootmenu">corresponding
instructions in the guide</a>.</p>
<p>Finally, a second choice has to be made between <code>rEFind</code>
or plain <code>efibootmgr</code> for managing the boot entries. I prefer
to go with the simpler one, but you may find <code>rEFind</code> more
feature-packed.</p>
<p>First, install <code>efibootmgr</code> using
<code>xbps-install efibootmgr</code>, then run the following
commands:</p>
<div class="sourceCode" id="cb31"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="ex">efibootmgr</span> <span class="at">-c</span> <span class="at">-d</span> <span class="st">&quot;</span><span class="va">$BOOT_DISK</span><span class="st">&quot;</span> <span class="at">-p</span> <span class="st">&quot;</span><span class="va">$BOOT_PART</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-L</span> <span class="st">&quot;ZFSBootMenu (Backup)&quot;</span> <span class="dt">\</span></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a> <span class="at">-l</span> <span class="st">&#39;\EFI\ZBM\VMLINUZ-BACKUP.EFI&#39;</span></span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a><span class="ex">efibootmgr</span> <span class="at">-c</span> <span class="at">-d</span> <span class="st">&quot;</span><span class="va">$BOOT_DISK</span><span class="st">&quot;</span> <span class="at">-p</span> <span class="st">&quot;</span><span class="va">$BOOT_PART</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a> <span class="at">-L</span> <span class="st">&quot;ZFSBootMenu&quot;</span> <span class="dt">\</span></span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a> <span class="at">-l</span> <span class="st">&#39;\EFI\ZBM\VMLINUZ.EFI&#39;</span></span></code></pre></div>
<p>If youd prefer to use rEFInd, see the <a
href="https://docs.zfsbootmenu.org/en/latest/guides/void-linux/uefi.html#configure-efi-boot-entries">guides
relevant section</a>.</p>
<p><code>zbm-kcl</code> is mentioned here in passing. This utility
allows you, among other things, to set ZFSBootMenu options, such as the
delay before automatically booting. I am not sure if it comes included
with the ZFSBootMenu package, as I went for the pre-built image, but you
can nonetheless get it from GitHub:</p>
<div class="sourceCode" id="cb32"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-O</span> https://raw.githubusercontent.com/zbm-dev/zfsbootmenu/master/bin/zbm-kcl</span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="fu">chmod</span> +x zbm-kcl</span></code></pre></div>
<p>Now, if you want to change an option, you can use its <code>-a</code>
option to append an argument to the target images command line:</p>
<div class="sourceCode" id="cb33"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zbm-kcl</span> <span class="at">-a</span> <span class="st">&#39;zbm.timeout=2&#39;</span> /boot/efi/EFI/ZBM/VMLINUZ.EFI</span></code></pre></div>
<p>In the example above, the timeout before automatically booting is set
from its 10 seconds default to 2 seconds.</p>
<h2 id="getting-out">Getting out</h2>
<p>We are all done. Its time to exit the chroot, unmount and export the
pool.</p>
<div class="sourceCode" id="cb34"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="bu">exit</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="fu">umount</span> <span class="at">-n</span> <span class="at">-R</span> /mnt</span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a><span class="ex">zpool</span> export zroot</span></code></pre></div>
<p>If all above went well, we can now run <code>reboot</code>, remove
the flash drive used for installation, and log in for the first time
into our new system.</p>
<h2 id="zfs-snapshot-basics">ZFS snapshot basics</h2>
<p>Something you might want to do at this point is to take a snapshot of
the current state, since it can serve as a baseline before any further
tweaking, allowing you to go back or access the files in this state as
you make important changes that could potentially break the system.</p>
<div class="sourceCode" id="cb35"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> snapshot <span class="at">-r</span> zroot/ROOT/void@baseline</span></code></pre></div>
<p>Note that, if you followed the ZFSBootMenu guide in creating a
separate dataset for your home directory, this snapshot will not include
the contents inside and under <code>/home</code> (which at this point
should be empty anyways).</p>
<p>You can access the contents of a snapshot at any time in the
<code>.zfs</code> directory at the root of a given dataset. For the ones
we previously set up, those would be <code>/.zfs</code> and
<code>/home/.zfs</code>. Note that these directories are not only hidden
in the traditional way, but they wont show up even if you use
<code>ls -a</code>. You need to actually <code>cd</code> into the
apparently absent directory for it to work.</p>
<p>ZFS snapshots start taking virtually no space at all, but grow with
time as the snapshot drifts from the present system state. For that
reason, keeping a snapshot of the very first moment of your system can
take up significant space. Depending on your storage resources, you
might eventually decide to destroy this snapshot:</p>
<div class="sourceCode" id="cb36"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> destroy <span class="at">-r</span> zroot/ROOT/void@baseline</span></code></pre></div>
<p>You may also want to list your current snapshots. While typically you
can use <code>zfs list -t snap</code>, I tend to use the following
command in order to get more relevant output:</p>
<div class="sourceCode" id="cb37"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> list <span class="at">-t</span> snap <span class="at">-o</span> creation,name,used,written,referenced,refcompressratio <span class="at">-S</span> creation</span></code></pre></div>
<p>Finally, you might want to rename a snapshot:</p>
<div class="sourceCode" id="cb38"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> rename <span class="at">-r</span> zroot/ROOT/void@baseline @day0</span></code></pre></div>
<p>Combined, these commands can get you as far as an automatic, rolling
snapshot system. Say, for instance you add the following to
<code>rc.local</code>:</p>
<div class="sourceCode" id="cb39"><pre
class="sourceCode sh"><code class="sourceCode bash"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> destroy <span class="at">-r</span> zroot/ROOT/void@fallbackBoot</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> rename <span class="at">-r</span> zroot/ROOT/void@previousBoot @fallbackBoot</span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> rename <span class="at">-r</span> zroot/ROOT/void@currentBoot @previousBoot</span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a><span class="ex">zfs</span> snapshot <span class="at">-r</span> zroot/ROOT/void@currentBoot</span></code></pre></div>
<p>This would give you a per-boot snapshot trail to rely on.</p>
<p>The <code>zfs-snapshot(8)</code> man page provides a similar example
for daily snapshots. Considering how simple the zfs CLI is, scripting
several snapshot schemes can be quite easy, be them per boot, daily, or
even every so many minutes using cron. Because ZFS snapshots grow as
they drift from the present state, rotating them is optimal when storage
space is a concern.</p>
<p>Thats it! I hope this was helpful to you in either learning about
ZFS or about Void installations with Root on ZFS.</p>
<hr />
<p><em>Originally written June 2nd, 2024</em></p>
</main class="article">
</body>
<footer>
<hr/>
<div class="footer-text">
<span id="author-pre-text">posted by</span>
<span class="author">Juno Takano</span>
<span id="date-pre-text">on</span>
<span class="date">June 9, 2024</span>
<span class="footer-back" aria-role="nav" aria-label="Navigate back">
<a id="footer-back-link" href="../index.html">Back</a>
</span>
</div>
</footer>
</html>