849 lines
62 KiB
HTML
Generated
849 lines
62 KiB
HTML
Generated
<!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. It’s 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 today’s
|
||
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, it’s
|
||
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 we’ll 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 you’ll 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 don’t 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 can’t
|
||
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">"Void"</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">"void"</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">"Void Linux"</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">"https://voidlinux.org/"</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">"https://docs.voidlinux.org/"</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">"void-logo"</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">"0;38;2;71;128;97"</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">"void</span></span></code></pre></div>
|
||
<p>For comparison, here is FreeBSD’s <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">"14.0-RELEASE"</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">"14.0"</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">"0;31"</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">"FreeBSD 14.0-RELEASE"</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">"cpe:/o:freebsd:freebsd:14.0"</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">"https://FreeBSD.org/"</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">"https://bugs.FreeBSD.org/"</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 can’t provide a real guarantee that it will be unique, so it’s up
|
||
to you to take care that it is unique among <em>your</em> machines. Read
|
||
on for why that’s 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 Void’s 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 system’s 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 I’ll
|
||
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">"</span><span class="va">$POOL_DISK</span><span class="st">"</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 "$BOOT_DISK"</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">"</span><span class="va">$POOL_DISK</span><span class="st">"</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">"</span><span class="va">$BOOT_DISK</span><span class="st">"</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. I’ll 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">"</span><span class="va">$POOL_DISK</span><span class="st">"</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">"</span><span class="va">$BOOT_DISK</span><span class="st">"</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">"</span><span class="va">${BOOT_PART}</span><span class="st">:1m:+512m"</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">"</span><span class="va">${BOOT_PART}</span><span class="st">:ef00"</span> <span class="st">"</span><span class="va">$BOOT_DISK</span><span class="st">"</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">"</span><span class="va">${POOL_PART}</span><span class="st">:0:-10m"</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">"</span><span class="va">${POOL_PART}</span><span class="st">:bf00"</span> <span class="st">"</span><span class="va">$POOL_DISK</span><span class="st">"</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, I’ve had issues in the past with the boot partition
|
||
being too small, so I’ll 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>Here’s 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, it’s 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>We’ll 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">"</span><span class="va">$POOL_DEVICE</span><span class="st">"</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 hasn’t 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 don’t. 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 you’d 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">"UTC"</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">"br-abnt2"</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">"ter-120n"</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"><</span>timezone<span class="op">></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">"yes"</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">" zfs "</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">" btrfs "</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. Let’s 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
|
||
manufacturer’s 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">"quiet net.ifnames=0 fbcon=nodefer video=efifb:nobgrt"</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">"</span><span class="va">$BOOT_DEVICE</span><span class="st">"</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">"</span><span class="va">$(</span><span class="ex">blkid</span> <span class="kw">|</span> <span class="fu">grep</span> <span class="st">"</span><span class="va">$BOOT_DEVICE</span><span class="st">"</span> <span class="kw">|</span> <span class="fu">cut</span> <span class="at">-d</span> <span class="st">' '</span> <span class="at">-f</span> 2<span class="va">)</span><span class="st"> /boot/efi vfat defaults 0 0"</span> <span class="op">>></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 I’d 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 you’d 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">"</span><span class="va">$BOOT_DISK</span><span class="st">"</span> <span class="at">-p</span> <span class="st">"</span><span class="va">$BOOT_PART</span><span class="st">"</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">"ZFSBootMenu (Backup)"</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">'\EFI\ZBM\VMLINUZ-BACKUP.EFI'</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">"</span><span class="va">$BOOT_DISK</span><span class="st">"</span> <span class="at">-p</span> <span class="st">"</span><span class="va">$BOOT_PART</span><span class="st">"</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">"ZFSBootMenu"</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">'\EFI\ZBM\VMLINUZ.EFI'</span></span></code></pre></div>
|
||
<p>If you’d prefer to use rEFInd, see the <a
|
||
href="https://docs.zfsbootmenu.org/en/latest/guides/void-linux/uefi.html#configure-efi-boot-entries">guide’s
|
||
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 image’s 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">'zbm.timeout=2'</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. It’s 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 won’t 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>That’s 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>
|