<?xml version="1.0" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <!-- _l: atom -->
  <!-- consumed by nixos.org's feed reader -->
<channel>
    <title>Graham Christensen</title>
    <link>http://grahamc.com/</link>
    <atom:link href="http://grahamc.com/feed/" rel="self" type="application/rss+xml" />
    <lastBuildDate>Fri, 10 Mar 2023 19:32:40 +0000</lastBuildDate>

    
    
    <item>
        <guid>http://grahamc.com//blog/nixos-on-framework/</guid>
        <title>NixOS on the Framework</title>
        <pubDate>Mon, 26 Jul 2021 00:00:00 +0000</pubDate>
        <description><![CDATA[<p><a href="/resources/2021-07-29-framework-nixos.png" target="_blank"><img src="/resources/2021-07-29-framework-nixos.small.png" alt="NixOS 21.11 with GNOME running on the Framework laptop." /></a></p>

<p>What a treat it is to review the <a href="https://frame.work" target="_blank">Framework</a> laptop a few months before I’ll be buying my own.</p>

<p>The Framework promises to be an powerful, high-end ultra thin laptop with the stand-out feature of easy repairs and upgrades. I think they’ve done it.</p>

<p>Let’s install NixOS.</p>

<h2 id="live-media">Live Media</h2>

<p>You’ll need an install image with Linux 5.13 or newer to have a working Wi-Fi card.</p>

<p>This presents a challenge: the ISOs provided by <a href="">https://nixos.org</a> today use Linux 5.10.</p>

<p>If you’re comfortable installing NixOS without a GUI, you can <a href="https://hydra.nixos.org/job/nixos/release-21.05/nixos.iso_minimal_new_kernel.x86_64-linux/latest">fetch a minimal 21.05 ISO with the latest kernel from Hydra</a>.</p>

<h3 id="optionally-building-our-own-live-media">Optionally, Building Our Own Live Media</h3>

<p>Building your own customized install media is straightforward. You’ll need a Linux machine with Nix.</p>

<p>First, write the following in to a file named <code class="language-plaintext highlighter-rouge">custom-media.nix</code>:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">pkgs</span><span class="p">,</span> <span class="nv">modulesPath</span><span class="p">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
    <span class="nv">imports</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s2">"</span><span class="si">${</span><span class="nv">modulesPath</span><span class="si">}</span><span class="s2">/installer/cd-dvd/installation-cd-graphical-gnome.nix"</span>
    <span class="p">];</span>

    <span class="nv">boot</span><span class="o">.</span><span class="nv">kernelPackages</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">linuxPackages_latest</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then enter a nix-shell with <code class="language-plaintext highlighter-rouge">nixos-generators</code> and build the media:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>nix-shell <span class="nt">-p</span> nixos-generators
<span class="gp">nix-shell$</span><span class="w"> </span>nixos-generate <span class="nt">-I</span> <span class="nv">nixpkgs</span><span class="o">=</span>channel:nixos-unstable <span class="nt">--format</span> iso <span class="nt">--configuration</span> ./custom-media.nix 
<span class="go">unpacking 'https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz'...
these derivations will be built:
  ...snip...
/nix/store/gnnbjvd916yh1f4svbgrssq94550pbxl-nixos-21.11pre304626.8ecc61c91a5-x86_64-linux.iso/iso/nixos-21.11pre304626.8ecc61c91a5-x86_64-linux.iso
</span></code></pre></div></div>

<p>Then copy the ISO to my USB disk, which is called <code class="language-plaintext highlighter-rouge">/dev/sda</code>.</p>

<p><em>Note: the <code class="language-plaintext highlighter-rouge">sync</code> at the end is critical.</em></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo cp /nix/store/gnnbjvd916yh1f4svbgrssq94550pbxl-nixos-21.11pre304626.8ecc61c91a5-x86_64-linux.iso/iso/nixos-21.11pre304626.8ecc61c91a5-x86_64-linux.iso /dev/sda
$ sudo sync
</code></pre></div></div>

<h2 id="disabling-secure-boot">Disabling Secure Boot</h2>

<ol>
  <li>Reboot</li>
  <li>Enter the Firmware Configuration (<code class="language-plaintext highlighter-rouge">Fn</code> + <code class="language-plaintext highlighter-rouge">F2</code>)</li>
  <li>Navigate to the <em>Security</em> tab</li>
  <li>Select <em>Secure Boot</em></li>
  <li>Select <em>Enforce Secure Boot</em></li>
  <li>Select <em>Disabled</em></li>
  <li>Save and reboot with <code class="language-plaintext highlighter-rouge">Fn</code> + <code class="language-plaintext highlighter-rouge">F10</code></li>
</ol>

<p><em>Note: Secure Boot will prevent unsigned software from running. NixOS does not support Secure Boot today.</em></p>

<h2 id="booting-your-media">Booting Your Media</h2>

<p>It appears the machine can boot from any USB port.</p>

<ol>
  <li>Reboot</li>
  <li>Enter the Boot Manager (<code class="language-plaintext highlighter-rouge">Fn</code> + <code class="language-plaintext highlighter-rouge">F12</code>)</li>
  <li>Select your USB disk</li>
</ol>

<h2 id="installing-nixos">Installing NixOS</h2>

<p>Follow the <a href="https://nixos.org/manual/nixos/stable/index.html#sec-installation">standard NixOS installation instructions</a>.</p>

<p>After running <code class="language-plaintext highlighter-rouge">nixos-generate-config</code>, edit <code class="language-plaintext highlighter-rouge">/mnt/etc/nixos/configuration.nix</code> and add a few lines:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">boot.kernelPackages = pkgs.linuxPackages_latest;</code> - for WiFi support</li>
  <li><code class="language-plaintext highlighter-rouge">services.fprintd.enable = true;</code> for fingerprint support</li>
</ul>

<p>Continue the installation procedure.</p>

<p>Seriously. That’s it.</p>

<hr />

<h1 id="hardware-review">Hardware Review</h1>

<p>The review unit they shipped me is pre-release hardware, but well specced out and lovely:</p>

<ul>
  <li>11th Gen Intel i7-1185G7, up to 4.8GHz</li>
  <li>16G of RAM</li>
  <li>Intel(R) Wi-Fi 6 AX210 160MHz, REV=0x420</li>
  <li>2 USB-C ports, a mini-display port, and a Micro SD slot</li>
</ul>

<p>Its performance seems quite good, easily good enough for my day to day work. It has no problems driving my 38” ultrawide display.</p>

<p>The machine is silent except when I’m compiling, and it has never felt hot. After a long compilation some areas near the hinge are noticably warm, but the hand rests are still cool.</p>

<p>The keyboard and trackpad feel, to me, perfect. The keys feel crisp but not heavy. The trackpad feels large and precise. That said, click is a bit <em>too</em> clicky.</p>

<p>Hardware switches for the audio and camera are nice, but a bit hard to toggle. The switches are very small and smooth, making it difficult to move. The switches are part of the replacable bezel, and this may be easily fixed.</p>

<p>The power brick is UL Energy Verified and just slightly larger than the Dell XPS 9300 brick.</p>

<p>The Intel Wi-Fi 6 AX210 has integration issues with Linux. Sometimes the wifi firmware crashes on startup, but it seems to be no more frequent than 50/50. Once it is booted, the wifi is rock solid.</p>

<h2 id="qr-codes-on-everything">QR Codes on Everything</h2>

<p><a href="/resources/2021-07-29-framework-inside.png" target="_blank"><img src="/resources/2021-07-29-framework-inside.small.png" alt="The inside of the laptop, with dozens of QR codes." /></a></p>

<p>Everything has a QR code as a link to instructions. And I mean everything: down to the cable connecting the wall to the power brick.</p>

<p>However, many of them don’t actually lead anywhere. I wasn’t too surprised to see the power cable’s QR code was 404ing, but a bit surprised to see the link for RAM replacement 404ing. I am sure this is a work in progress and these QR codes will all work.</p>

<h2 id="relative-to-the-xps-9300">Relative to the XPS 9300</h2>

<p>I want the smallest, lightest laptop I can buy, and the Framework is a strong contender here.</p>

<p>This laptop is essentially the same width and thickness: within a milimeter or two. The framework comes in at just 60 grams heavier than the XPS (1330g vs. 1270g.)</p>

<p>One major improvement over the 9300: The fingerprint sensor is supported by recent versions of fprintd out of the box, and no TOD patches are required. The sensor is fast and reliable, whereas the Dell XPS 9300’s TOD Goodix drivers are nearly worthless on Linux.</p>

<p>Some aspects of the Framework are a bit more “industrial” feeling:</p>

<ul>
  <li>The cable from the brick to the wall looks like a desktop PC’s cable. Thick, shiny plastic. compared to the more polished, polished, on-brand look of the Dell cable.</li>
  <li>The rubber feet are chunky, and not super sticky.</li>
  <li>Opening the lid is not <em>super</em> easy like on the XPS.</li>
</ul>

<h1 id="ready-to-buy">Ready to Buy</h1>

<p>Overall, I am extremely satisfied by Framework. The team seems great, and their values appear to match my own. I’ll be pulling out my wallet later this year when my current laptop isn’t so new. I think they’ve fulfilled their promise, and have created a refreshing machine: an ultralight which is servicable.</p>

<p>Yes, please.</p>
]]></description>
        <link>http://grahamc.com/blog/nixos-on-framework/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/flakes-are-an-obviously-good-thing/</guid>
        <title>Flakes are such an obviously good thing</title>
        <pubDate>Sat, 08 May 2021 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Flakes is a <em>major</em> development for Nix. I believe one of the most significant changes the project has ever seen. Flakes brings a standardized interchange format for expressions, and dramatically reduces the friction of depending on someone else’s code. However, it needs the community involved to shape and evolve in to a final and wonderful tool.</p>

<p>The Nix community is about 18 years old now. Until recently (~6 years ago) the community was quite small. The project is now much bigger, and growing. The result is that, organizationally, we’re a bit immature. The RFC process is new, and only a few major changes have gone through it.</p>

<p>Unfortunately, Flakes was one of the very first major Nix features to go through the community’s relatively young RFC process.</p>

<p>As a community, we’re not accustomed to and practiced at breaking down large and fundamental changes to the ecosystem and shepherding them through RFCs. But there we were: we had a shiny new process and by golly, a change that deserves an RFC! We hadn’t even tied our shoes and yet we were attempting our first triathalon.</p>

<p>I believe everybody approached that RFC with really good intentions and hope that it would go well and be a productive process. In a lot of ways, it was productive. But the end of that RFC was not good.</p>

<p>What was one RFC probably should have been very theoretical “this is an idea, should we explore it?” followed by several RFCs about specific subsections and details about how Flakes would work.</p>

<p>But… we were so new to RFCs, so new to being a large project, we didn’t see the problems coming.</p>

<p>As a result, the RFC was closed, agreeing to make Flakes “experimental” but merge it into Nix anyway. Maybe in the future, a new RFC would be submitted to review the code as existed in Nix already.</p>

<p>This has caused quite a lot of bad feelings between all sorts of people. A lot of assumptions about motivations that I don’t believe hold up. Probably a good bit of distrust in to RFCs and the legitimacy of the process.</p>

<p>I find this so unfortunate. I believe Flakes are astonishingly important and will be very powerful, but the RFC experience has turned so many people against it on principle. Flakes have a lot of potential, and needs a strong community to work through their problems and help it flourish.</p>

<p>The damage to the perception of RFCs is real and tragic. I believe so deeply in this project and its ability to grow, and the organizational distrust from this makes that much, much more difficult.</p>

<p>I feel so disappointed in myself for not seeing the dangers of sending such a fundamental change to Nix the tool through a nearly brand new process which was a fundamental change to the Nix community. The process wasn’t ready for it, the participants weren’t ready for it.</p>

<p>I regret that so much.</p>

<p>I believe we can, as a project and community, move past it. It will take leadership and effort from a lot of people. Project leadership will have to make the first moves there, to mend the distrust and sow new seeds of cooperation. I know we can do this.</p>

<p>I have so much love for this project and its community. I feel so grateful to be part of it, surrounded every day by people so much smarter than me.</p>

<p>I hope to be part of the solution, to be part of the healing and growth.</p>

<p>I have been part of this project for just over five years now, and I am incredibly excited to be part of the next five.</p>

<p>The future of Nix is so bright I can hardly look right at it without looking down at less ambitious futures.</p>

<p><em>This was originally a series of tweets.</em></p>
]]></description>
        <link>http://grahamc.com/blog/flakes-are-an-obviously-good-thing/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/erase-your-darlings/</guid>
        <title>Erase your darlings</title>
        <pubDate>Mon, 13 Apr 2020 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I erase my systems at every boot.</p>

<p>Over time, a system collects state on its root partition. This state
lives in assorted directories like <code class="language-plaintext highlighter-rouge">/etc</code> and <code class="language-plaintext highlighter-rouge">/var</code>, and represents
every under-documented or out-of-order step in bringing up the
services.</p>

<blockquote>
  <p>“Right, run <code class="language-plaintext highlighter-rouge">myapp-init</code>.”</p>
</blockquote>

<p>These small, inconsequential “oh, oops” steps are the pieces that get
lost and don’t appear in your runbooks.</p>

<blockquote>
  <p>“Just download ca-certificates to … to fix …”</p>
</blockquote>

<p>Each of these quick fixes leaves you doomed to repeat history in three
years when you’re finally doing that dreaded RHEL 7 to RHEL 8 upgrade.</p>

<blockquote>
  <p>“Oh, <code class="language-plaintext highlighter-rouge">touch /etc/ipsec.secrets</code> or the l2tp tunnel won’t work.”</p>
</blockquote>

<h3 id="immutable-infrastructure-gets-us-so-close">Immutable infrastructure gets us <em>so</em> close</h3>

<p>Immutable infrastructure is a wonderfully effective method of
eliminating so many of these forgotten steps. Leaning in to the pain
by deleting and replacing your servers on a weekly or monthly basis
means you are constantly testing and exercising your automation and
runbooks.</p>

<p>The nugget here is the regular and indiscriminate removal of system
state. Destroying the whole server doesn’t leave you much room to
forget the little tweaks you made along the way.</p>

<p>These techniques work great when you meet two requirements:</p>

<ul>
  <li>you can provision and destroy servers with an API call</li>
  <li>the servers aren’t inherently stateful</li>
</ul>

<h4 id="long-running-servers">Long running servers</h4>

<p>There are lots of cases in which immutable infrastructure <em>doesn’t</em>
work, and the dirty secret is <strong>those servers need good tools the
most.</strong></p>

<p>Long-running servers cause long outages. Their runbooks are outdated
and incomplete. They accrete tweaks and turn in to an ossified,
brittle snowflake — except its arms are load-bearing.</p>

<p>Let’s bring the ideas of immutable infrastructure to these systems
too. Whether this system is embedded in a stadium’s jumbotron, in a
datacenter, or under your desk, we <em>can</em> keep the state under control.</p>

<h4 id="fhs-isnt-enough">FHS isn’t enough</h4>

<p>The hard part about applying immutable techniques to long running
servers is knowing exactly where your application state ends and the
operating system, software, and configuration begin.</p>

<p>This is hard because legacy operating systems and the Filesystem
Hierarchy Standard poorly separate these areas of concern. For
example, <code class="language-plaintext highlighter-rouge">/var/lib</code> is for state information, but how much of this do
you actually care about tracking? What did you configure in <code class="language-plaintext highlighter-rouge">/etc</code> on
purpose?</p>

<p>The answer is probably not a lot.</p>

<p>You may not care, but all of this accumulation of junk is a tarpit.
Everything becomes harder: replicating production, testing changes,
undoing mistakes.</p>

<h3 id="new-computer-smell">New computer smell</h3>

<p>Getting a new computer is this moment of cleanliness. The keycaps
don’t have oils on them, the screen is perfect, and the hard drive
is fresh and unspoiled — for about an hour or so.</p>

<p>Let’s get back to that.</p>

<h2 id="how-is-this-possible">How is this possible?</h2>

<p>NixOS can boot with only two directories: <code class="language-plaintext highlighter-rouge">/boot</code>, and <code class="language-plaintext highlighter-rouge">/nix</code>.</p>

<p><code class="language-plaintext highlighter-rouge">/nix</code> contains read-only system configurations, which are specified
by your <code class="language-plaintext highlighter-rouge">configuration.nix</code> and are built and tracked as system
generations. These never change. Once the files are created in <code class="language-plaintext highlighter-rouge">/nix</code>,
the only way to change the config’s contents is to build a new system
configuration with the contents you want.</p>

<p>Any configuration or files created on the drive outside of <code class="language-plaintext highlighter-rouge">/nix</code> is
state and cruft. We can lose everything outside of <code class="language-plaintext highlighter-rouge">/nix</code> and <code class="language-plaintext highlighter-rouge">/boot</code>
and have a healthy system. My technique is to explicitly opt in and
<em>choose</em> which state is important, and only keep that.</p>

<p>How this is possible comes down to the boot sequence.</p>

<p>For NixOS, the bootloader follows the same basic steps as a standard
Linux distribution: the kernel starts with an initial ramdisk, and the
initial ramdisk mounts the system disks.</p>

<p>And here is where the similarities end.</p>

<h3 id="nixoss-early-startup">NixOS’s early startup</h3>

<p>NixOS configures the bootloader to pass some extra information: a
specific system configuration. This is the secret to NixOS’s
bootloader rollbacks, and also the key to erasing our disk on each
boot. The parameter is named <code class="language-plaintext highlighter-rouge">systemConfig</code>.</p>

<p>On every startup the very early boot stage knows what the system’s
configuration should be: the entire system configuration is stored in
the read-only <code class="language-plaintext highlighter-rouge">/nix/store</code>, and the directory passed through
<code class="language-plaintext highlighter-rouge">systemConfig</code> has a reference to the config. Early boot then
manipulates <code class="language-plaintext highlighter-rouge">/etc</code> and <code class="language-plaintext highlighter-rouge">/run</code> to match the chosen setup. Usually this
involves swapping out a few symlinks.</p>

<p>If <code class="language-plaintext highlighter-rouge">/etc</code> simply doesn’t exist, however, early boot <em>creates</em> <code class="language-plaintext highlighter-rouge">/etc</code>
and moves on like it were any other boot. It also <em>creates</em> <code class="language-plaintext highlighter-rouge">/var</code>,
<code class="language-plaintext highlighter-rouge">/dev</code>, <code class="language-plaintext highlighter-rouge">/home</code>, and any other core directories that must be present.</p>

<p>Simply speaking, an empty <code class="language-plaintext highlighter-rouge">/</code> is <em>not surprising</em> to NixOS. In fact,
the NixOS netboot, EC2, and installation media all start out this way.</p>

<h2 id="opting-out">Opting out</h2>

<p>Before we can opt in to saving data, we must opt out of saving data
<em>by default</em>. I do this by setting up my filesystem in a way that
lets me easily and safely erase the unwanted data, while preserving
the data I do want to keep.</p>

<p>My preferred method for this is using a ZFS dataset and rolling it
back to a blank snapshot before it is mounted. A partition of any
other filesystem would work just as well too, running <code class="language-plaintext highlighter-rouge">mkfs</code> at boot,
or something similar. If you have a lot of RAM, you could skip the
erase step and make <code class="language-plaintext highlighter-rouge">/</code> a tmpfs.</p>

<h3 id="opting-out-with-zfs">Opting out with ZFS</h3>
<p>When installing NixOS, I partition my disk with two partitions, one
for the boot partition, and another for a ZFS pool. Then I create and
mount a few datasets.</p>

<p>My root dataset:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># zfs create -p -o mountpoint=legacy rpool/local/root
</code></pre></div></div>

<p>Before I even mount it, I <strong>create a snapshot while it is totally
blank</strong>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># zfs snapshot rpool/local/root@blank
</code></pre></div></div>

<p>And then mount it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mount -t zfs rpool/local/root /mnt
</code></pre></div></div>

<p>Then I mount the partition I created for the <code class="language-plaintext highlighter-rouge">/boot</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir /mnt/boot
# mount /dev/the-boot-partition /mnt/boot
</code></pre></div></div>

<p>Create and mount a dataset for <code class="language-plaintext highlighter-rouge">/nix</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># zfs create -p -o mountpoint=legacy rpool/local/nix
# mkdir /mnt/nix
# mount -t zfs rpool/local/nix /mnt/nix
</code></pre></div></div>

<p>And a dataset for <code class="language-plaintext highlighter-rouge">/home</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># zfs create -p -o mountpoint=legacy rpool/safe/home
# mkdir /mnt/home
# mount -t zfs rpool/safe/home /mnt/home
</code></pre></div></div>

<p>And finally, a dataset explicitly for state I want to persist between
boots:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># zfs create -p -o mountpoint=legacy rpool/safe/persist
# mkdir /mnt/persist
# mount -t zfs rpool/safe/persist /mnt/persist
</code></pre></div></div>

<blockquote>
  <p><em>Note:</em> in my systems, datasets under <code class="language-plaintext highlighter-rouge">rpool/local</code> are never backed
up, and datasets under <code class="language-plaintext highlighter-rouge">rpool/safe</code> are.</p>
</blockquote>

<p>And now safely erasing the root dataset on each boot is very easy:
after devices are made available, roll back to the blank snapshot:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">boot</span><span class="o">.</span><span class="nv">initrd</span><span class="o">.</span><span class="nv">postDeviceCommands</span> <span class="o">=</span> <span class="nv">lib</span><span class="o">.</span><span class="nv">mkAfter</span> <span class="s2">''</span><span class="err">
</span><span class="s2">    zfs rollback -r rpool/local/root@blank</span><span class="err">
</span><span class="s2">  ''</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I then finish the installation as normal. If all goes well, your
next boot will start with an empty root partition but otherwise be
configured exactly as you specified.</p>

<h2 id="opting-in">Opting in</h2>

<p>Now that I’m keeping no state, it is time to specify what I do want
to keep. My choices here are different based on the role of the
system: a laptop has different state than a server.</p>

<p>Here are some different pieces of state and how I preserve them. These
examples largely use reconfiguration or symlinks, but using ZFS
datasets and mount points would work too.</p>

<h4 id="wireguard-private-keys">Wireguard private keys</h4>

<p>Create a directory under <code class="language-plaintext highlighter-rouge">/persist</code> for the key:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /persist/etc/wireguard/
</code></pre></div></div>

<p>And use Nix’s wireguard module to generate the key there:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">networking</span><span class="o">.</span><span class="nv">wireguard</span><span class="o">.</span><span class="nv">interfaces</span><span class="o">.</span><span class="nv">wg0</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">generatePrivateKeyFile</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="nv">privateKeyFile</span> <span class="o">=</span> <span class="s2">"/persist/etc/wireguard/wg0"</span><span class="p">;</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="networkmanager-connections">NetworkManager connections</h4>

<p>Create a directory under <code class="language-plaintext highlighter-rouge">/persist</code>, mirroring the <code class="language-plaintext highlighter-rouge">/etc</code> structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /persist/etc/NetworkManager/system-connections
</code></pre></div></div>

<p>And use Nix’s <code class="language-plaintext highlighter-rouge">etc</code> module to set up the symlink:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">etc</span><span class="o">.</span><span class="s2">"NetworkManager/system-connections"</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">source</span> <span class="o">=</span> <span class="s2">"/persist/etc/NetworkManager/system-connections/"</span><span class="p">;</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="bluetooth-devices">Bluetooth devices</h4>

<p>Create a directory under <code class="language-plaintext highlighter-rouge">/persist</code>, mirroring the <code class="language-plaintext highlighter-rouge">/var</code> structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /persist/var/lib/bluetooth
</code></pre></div></div>

<p>And then use systemd’s tmpfiles.d rules to create a symlink from
<code class="language-plaintext highlighter-rouge">/var/lib/bluetooth</code> to my persisted directory:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">systemd</span><span class="o">.</span><span class="nv">tmpfiles</span><span class="o">.</span><span class="nv">rules</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s2">"L /var/lib/bluetooth - - - - /persist/var/lib/bluetooth"</span>
  <span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="ssh-host-keys">SSH host keys</h4>

<p>Create a directory under <code class="language-plaintext highlighter-rouge">/persist</code>, mirroring the <code class="language-plaintext highlighter-rouge">/etc</code> structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /persist/etc/ssh
</code></pre></div></div>

<p>And use Nix’s openssh module to create and use the keys in that
directory:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">services</span><span class="o">.</span><span class="nv">openssh</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="nv">hostKeys</span> <span class="o">=</span> <span class="p">[</span>
      <span class="p">{</span>
        <span class="nv">path</span> <span class="o">=</span> <span class="s2">"/persist/ssh/ssh_host_ed25519_key"</span><span class="p">;</span>
        <span class="nv">type</span> <span class="o">=</span> <span class="s2">"ed25519"</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="p">{</span>
        <span class="nv">path</span> <span class="o">=</span> <span class="s2">"/persist/ssh/ssh_host_rsa_key"</span><span class="p">;</span>
        <span class="nv">type</span> <span class="o">=</span> <span class="s2">"rsa"</span><span class="p">;</span>
        <span class="nv">bits</span> <span class="o">=</span> <span class="mi">4096</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">];</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="acme-certificates">ACME certificates</h4>

<p>Create a directory under <code class="language-plaintext highlighter-rouge">/persist</code>, mirroring the <code class="language-plaintext highlighter-rouge">/var</code> structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /persist/var/lib/acme
</code></pre></div></div>

<p>And then use systemd’s tmpfiles.d rules to create a symlink from
<code class="language-plaintext highlighter-rouge">/var/lib/acme</code> to my persisted directory:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">systemd</span><span class="o">.</span><span class="nv">tmpfiles</span><span class="o">.</span><span class="nv">rules</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s2">"L /var/lib/acme - - - - /persist/var/lib/acme"</span>
  <span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="answering-the-question-what-am-i-about-to-lose">Answering the question “what am I about to lose?”</h3>

<p>I found this process a bit scary for the first few weeks: was I losing
important data each reboot? No, I wasn’t.</p>

<p>If you’re worried and want to know what state you’ll lose on the next
boot, you can list the files on your root filesystem and see if you’re
missing something important:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># tree -x /
├── bin
│   └── sh -&gt; /nix/store/97zzcs494vn5k2yw-dash-0.5.10.2/bin/dash
├── boot
├── dev
├── etc
│   ├── asound.conf -&gt; /etc/static/asound.conf
... snip ...
</code></pre></div></div>

<p>ZFS can give you a similar answer:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># zfs diff rpool/local/root@blank
M	/
+	/nix
+	/etc
+	/root
+	/var/lib/is-nix-channel-up-to-date
+	/etc/pki/fwupd
+	/etc/pki/fwupd-metadata
... snip ...
</code></pre></div></div>

<h2 id="your-stateless-future">Your stateless future</h2>

<p>You may bump in to new state you meant to be preserving. When I’m
adding new services, I think about the state it is writing and whether
I care about it or not. If I care, I find a way to redirect its state
to <code class="language-plaintext highlighter-rouge">/persist</code>.</p>

<p>Take care to reboot these machines on a somewhat regular basis. It
will keep things agile, proving your system state is tracked
correctly.</p>

<p>This technique has given me the “new computer smell” on every boot
without the datacenter full of hardware, and even on systems that do
carry important state. I have deployed this strategy to systems in the
large and small: build farm servers, database servers, my NAS and home
server, my raspberry pi garage door opener, and laptops.</p>

<p>NixOS enables powerful new deployment models in so many ways, allowing
for systems of all shapes and sizes to be managed properly and
consistently. I think this model of ephemeral roots is yet
another example of this flexibility and power. I would like to see
this partitioning scheme become a reference architecture and take us
out of this eternal tarpit of legacy.</p>
]]></description>
        <link>http://grahamc.com/blog/erase-your-darlings/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/nixos-on-zfs/</guid>
        <title>ZFS Datasets for NixOS</title>
        <pubDate>Sat, 11 Apr 2020 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>The outdated and historical nature of the <a href="fhs">Filesystem Hierarchy
Standard</a> means traditional Linux distributions have to go to great
lengths to separate “user data” from “system data.”</p>

<p>NixOS’s filesystem architecture does cleanly separate user data from
system data, and has a much easier job to do.</p>

<h3 id="traditional-linuxes">Traditional Linuxes</h3>

<p>Because FHS mixes these two concerns across the entire hierarchy,
splitting these concerns requires identifying every point across
dozens of directories where the data is the system’s or the user’s.
When adding ZFS to the mix, the installers typically have to create
over a dozen datasets to accomplish this.</p>

<p>For example, Ubuntu’s upcoming ZFS support creates 16 datasets:</p>

<pre><code class="language-tree">rpool/
├── ROOT
│   └── ubuntu_lwmk7c
│       ├── log
│       ├── mail
│       ├── snap
│       ├── spool
│       ├── srv
│       ├── usr
│       │   └── local
│       ├── var
│       │   ├── games
│       │   └── lib
│       │       ├── AccountServices
│       │       ├── apt
│       │       ├── dpkg
│       │       └── NetworkManager
│       └── www
└── USERDATA
</code></pre>

<p>Going through the great pains of separating this data comes with
significant advantages: a recursive snapshot at any point in the tree
will create an atomic, point-in-time snapshot of every dataset below.</p>

<p>This means in order to create a consistent snapshot of the system
data, an administrator would only need to take a recursive snapshot
at <code class="language-plaintext highlighter-rouge">ROOT</code>. The same is true for user data: take a recursive snapshot of
<code class="language-plaintext highlighter-rouge">USERDATA</code> and all user data is saved.</p>

<h3 id="nixos">NixOS</h3>

<p>Because Nix stores all of its build products in <code class="language-plaintext highlighter-rouge">/nix/store</code>, NixOS
doesn’t mingle these two concerns. NixOS’s runtime system, installed
packages, and rollback targets are all stored in <code class="language-plaintext highlighter-rouge">/nix</code>.</p>

<p>User data is not.</p>

<p>This removes the entire complicated tree of datasets to facilitate
FHS, and leaves us with only a few needed datasets.</p>

<h2 id="datasets">Datasets</h2>

<p>Design for the atomic, recursive snapshots when laying out the
datasets.</p>

<p>In particular, I don’t back up the <code class="language-plaintext highlighter-rouge">/nix</code> directory. This entire
directory can always be rebuilt later from the system’s
<code class="language-plaintext highlighter-rouge">configuration.nix</code>, and isn’t worth the space.</p>

<p>One way to model this might be splitting up the data into three
top-level datasets:</p>

<pre><code class="language-tree">tank/
├── local
│   └── nix
├── system
│   └── root
└── user
    └── home
</code></pre>

<p>In <code class="language-plaintext highlighter-rouge">tank/local</code>, I would store datasets that should almost never be
snapshotted or backed up. <code class="language-plaintext highlighter-rouge">tank/system</code> would store data that I would
want periodic snapshots for. Most importantly, <code class="language-plaintext highlighter-rouge">tank/user</code> would
contain data I want regular snapshots and backups for, with a long
retention policy.</p>

<p>From here, you could add a ZFS dataset per user:</p>

<pre><code class="language-tree">tank/
├── local
│   └── nix
├── system
│   └── root
└── user
    └── home
        ├── grahamc
        └── gustav
</code></pre>

<p>Or a separate dataset for <code class="language-plaintext highlighter-rouge">/var</code>:</p>

<pre><code class="language-tree">tank/
├── local
│   └── nix
├── system
│   ├── var
│   └── root
└── user
</code></pre>

<p>Importantly, this gives you three buckets for independent and
regular snapshots.</p>

<p>The important part is having <code class="language-plaintext highlighter-rouge">/nix</code> under its own top-level dataset.
This makes it a “cousin” to the data you <em>do</em> want backup coverage on,
making it easier to take deep, recursive snapshots atomically.</p>

<h2 id="properties">Properties</h2>

<ul>
  <li>Enable compression with <code class="language-plaintext highlighter-rouge">compression=on</code>. Specifying <code class="language-plaintext highlighter-rouge">on</code> instead of
<code class="language-plaintext highlighter-rouge">lz4</code> or another specific algorithm will always pick the best
available compression algorithm.</li>
  <li>The dataset containing journald’s logs (where <code class="language-plaintext highlighter-rouge">/var</code> lives) should
have <code class="language-plaintext highlighter-rouge">xattr=sa</code> and <code class="language-plaintext highlighter-rouge">acltype=posixacl</code> set to allow regular users to
read their journal.</li>
  <li>Nix doesn’t use <code class="language-plaintext highlighter-rouge">atime</code>, so <code class="language-plaintext highlighter-rouge">atime=off</code> on the <code class="language-plaintext highlighter-rouge">/nix</code> dataset is
fine.</li>
  <li>NixOS requires (as of 2020-04-11) <code class="language-plaintext highlighter-rouge">mountpoint=legacy</code> for all
datasets. NixOS does not yet have tooling to require implicitly
created ZFS mounts to settle before booting, and <code class="language-plaintext highlighter-rouge">mountpoint=legacy</code>
plus explicit mount points in <code class="language-plaintext highlighter-rouge">hardware-configuration.nix</code> will
ensure all your datasets are mounted at the right time.</li>
</ul>

<p>I don’t know how to pick <code class="language-plaintext highlighter-rouge">ashift</code>, and usually just allow ZFS to guess
on my behalf.</p>

<h2 id="partitioning">Partitioning</h2>

<p>I only create two partitions:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">/boot</code> formatted <code class="language-plaintext highlighter-rouge">vfat</code> for EFI, or <code class="language-plaintext highlighter-rouge">ext4</code> for BIOS</li>
  <li>The ZFS dataset partition.</li>
</ol>

<p>There are spooky articles saying only give ZFS entire disks. The
truth is, you shouldn’t split a disk into two active partitions.
Splitting the disk this way is just fine, since <code class="language-plaintext highlighter-rouge">/boot</code> is rarely
read or written.</p>

<blockquote>
  <p><em>Note:</em> If you do partition the disk, make sure you set the disk’s
scheduler to <code class="language-plaintext highlighter-rouge">none</code>. ZFS takes this step automatically if it does
control the entire disk.</p>

  <p>On NixOS, you an set your scheduler to <code class="language-plaintext highlighter-rouge">none</code> via:</p>

  <div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">boot</span><span class="o">.</span><span class="nv">kernelParams</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"elevator=none"</span> <span class="p">];</span> <span class="p">}</span>
</code></pre></div>  </div>
</blockquote>

<h1 id="clean-isolation">Clean isolation</h1>

<p>NixOS’s clean separation of concerns reduces the amount of complexity
we need to track when considering and planning our datasets. This
gives us flexibility later, and enables some superpowers like erasing
my computer on every boot, which I’ll write about on Monday.</p>

]]></description>
        <link>http://grahamc.com/blog/nixos-on-zfs/</link>
    </item>
	
    
    
    
    
    
    <item>
        <guid>http://grahamc.com//blog/nix-and-layered-docker-images/</guid>
        <title>Optimising Docker Layers for Better Caching with Nix</title>
        <pubDate>Mon, 01 Oct 2018 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Nix users value its isolated, repeatable builds and simple sharing of
development environments. Nix makes it easy to go back in time and
rebuild software from years ago without issue.</p>

<p>At the same time, the value of the container ecosystem is huge. Tying
in to the schedulers, orchestration, and monitoring is very valuable.</p>

<p>Nix has been able to generate Docker images for several years now,
however the typical approach to layering with Nix is to generate one
fat image with all of the dependencies. This fat image offers no
sharing, is slow to build, upload, and download.</p>

<p>In this post I talk about how I fix this problem and use Nix to
automatically create multi-layered Docker images, allowing a high
amount of caching between images.</p>

<h3 id="docker-uses-layers">Docker uses layers</h3>

<p>Docker’s use of layering is well known, and its benefits are
undeniable: sharing a “base” system is a simple abstraction which
allows extending a well known image with your own code.</p>

<p>A Docker image is a sequence of layers, where each member is a
filesystem diff, adding and removing files from its parent
member:</p>

<p><img src="/resources/2018-09-28-docker-images/docker.svg" alt="" /></p>

<h3 id="efficient-layering-is-hard-because-there-are-no-rules">Efficient layering is hard because there are no rules</h3>

<p>When there are no restrictions on what a command will do, the only way
to fully capture its effects is to snapshot the full filesystem.</p>

<p>Most package managers will write files to shared global directories
like <code class="language-plaintext highlighter-rouge">/usr</code>, <code class="language-plaintext highlighter-rouge">/bin</code>, and <code class="language-plaintext highlighter-rouge">/etc</code>.</p>

<p>This means that the <em>only</em> way to represent the changes between
installing package A and installing package B is to take a full
snapshot of the filesystem.</p>

<p>As a user you might manually create rules to improve the behavior of
the cache: add your code towards the end of a Dockerfile, or install
common libraries in a single <code class="language-plaintext highlighter-rouge">RUN</code> instruction, even if you don’t want
them all.</p>

<p>These rules make sense: If a Dockerfile adds code and then installs
packages, Docker can’t cache the installation because it can’t know
that the package installation isn’t influenced by the code addition.
Docker also can’t know that installing package A has nothing to do
with package B and the changes are separately cachable.</p>

<h3 id="with-restrictions-we-can-make-better-optimisations">With restrictions, we can make better optimisations</h3>

<p>Nix does have rules.</p>

<p>The most important and relevant rule when considering distribution and
Docker layers is:</p>

<blockquote>
  <p>A package build can’t write to arbitrary places on the disk.</p>
</blockquote>

<p>A build can only write to a specific directory known as <code class="language-plaintext highlighter-rouge">$out</code>, like
<code class="language-plaintext highlighter-rouge">/nix/store/ibfx7ryqnqf01qfzj4v7qhzhkd2v9mm7-file-5.34</code>. When you add
a new package to your system, you know it didn’t modify <code class="language-plaintext highlighter-rouge">/etc</code> or
<code class="language-plaintext highlighter-rouge">/bin</code>.</p>

<p>How does <code class="language-plaintext highlighter-rouge">file</code> find its dependencies? It doesn’t – they are
hard-coded:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ldd /nix/store/ibfx7ryqnqf01qfzj4v7qhzhkd2v9mm7-file-5.34/bin/file
	linux-vdso.so.1
	/nix/store/ibfx7ryqnqf01qfzj4v7qhzhkd2v9mm7-file-5.34/lib/libmagic.so.1
	/nix/store/bv6znzsv2qkbcwwa251dx7n5dshz3nr3-zlib-1.2.11/lib/libz.so.1
	/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/libc.so.6
	/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/ld-linux-x86-64.so.2
</code></pre></div></div>

<p>This provides great, cache-friendly properties:</p>

<ol>
  <li>You know exactly what path changed when you added <code class="language-plaintext highlighter-rouge">file</code>.</li>
  <li>You know exactly what paths <code class="language-plaintext highlighter-rouge">file</code> depends on.</li>
  <li>Once a path is created, it will never change again.</li>
</ol>

<h3 id="think-graphs-not-layers">Think graphs, not layers</h3>

<p>If you consider the properties Nix provides, you can see it already
constructs a graph internally to represent software and its
dependencies: it natively has a better understanding of the software
than Docker is interested in.</p>

<p>Specifically, Nix uses a Directed Acyclic Graph to store build output,
where each node is a specific, unique, and immutable path in
<code class="language-plaintext highlighter-rouge">/nix/store</code>:</p>

<p><img src="/resources/2018-09-28-docker-images/nix.svg" alt="" /></p>

<p>Or to use a real example, Nix itself can render a graph of a package’s
dependencies:</p>

<p><img src="/resources/2018-09-28-docker-images/file.svg" alt="" /></p>

<h3 id="flattening-graphs-to-layers">Flattening Graphs to Layers</h3>

<p>In a naive world we can simply walk the tree and create a layer out of
each path:</p>

<p><img src="/resources/2018-09-28-docker-images/flattened-file.svg" alt="" /></p>

<p>and this image is valid: if you pulled any of these layers, you
would automatically get all the layers below it, resulting in a
complete set of dependencies.</p>

<p>Things get a bit more complicated for a graph with a wider graph, how
do you flatten something like Bash:</p>

<p><img src="/resources/2018-09-28-docker-images/bash.svg" alt="" /></p>

<p>If we had to flatten this to an ordered sequence, obviously
<code class="language-plaintext highlighter-rouge">bash-interactive-4.4-p23</code> is at the top, but does <code class="language-plaintext highlighter-rouge">readline-7.0p5</code>
come next? Why not <code class="language-plaintext highlighter-rouge">bash-4.4p23</code>?</p>

<p>It turns out we don’t have to solve this problem exactly, because I
lied about how Docker represents layers.</p>

<h4 id="how-docker-really-represents-an-image">How Docker <em>really</em> represents an Image</h4>

<p>Docker’s layers are <em>content addressable</em> and aren’t required to
explicitly reference a parent layer. This means a layer for
<code class="language-plaintext highlighter-rouge">readline-7.0p5</code> doesn’t have to mention that it has any
relationship to <code class="language-plaintext highlighter-rouge">ncurses-6.1</code> or <code class="language-plaintext highlighter-rouge">glibc-2.27</code> at all.</p>

<p>Instead each image has a <em>manifest</em> which defines the order:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Layers"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"bash-interactive-4.4-p23"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"bash-4.4p23"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"readline-7.0p5"</span><span class="p">,</span><span class="w">
     </span><span class="err">...</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>If you have only built Docker images using a Dockerfile, then you
would expect the way we flatten our graph to be critically important.
If we sometimes picked <code class="language-plaintext highlighter-rouge">readline-7.0p5</code> to come first and other
times picked <code class="language-plaintext highlighter-rouge">bash-4.4p23</code> then we may never make cache hits.</p>

<p>However since the <em>Image</em> defines the order, we don’t have to solve
this impossible problem: we can order the layers in any way we want
and the layer cache will always hit.</p>

<h3 id="docker-doesnt-support-an-infinite-number-of-layers">Docker doesn’t support an infinite number of layers</h3>

<p>Docker has a <a href="https://github.com/moby/moby/blob/b3e9f7b13b0f0c414fa6253e1f17a86b2cff68b5/layer/layer_store.go#L23-L26">limit of 125 layers</a>, but big packages with
lots of dependencies can easily have more than 125 store paths.</p>

<p>It is important that we still successfully build an image if we go
over this limit, but what do we do with the extra layers?</p>

<p>In the interest of shortness, let’s pretend Docker only lets you have
<strong>four</strong> layers, and we want to fit five. Out of the Bash example,
which two paths do we combine in to one layer?</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">bash-interactive-4.4-p23</code></li>
  <li><code class="language-plaintext highlighter-rouge">bash-4.4p23</code></li>
  <li><code class="language-plaintext highlighter-rouge">readline-7.0p5</code></li>
  <li><code class="language-plaintext highlighter-rouge">ncurses-6.1</code></li>
  <li><code class="language-plaintext highlighter-rouge">glibc-2.27</code></li>
</ul>

<h4 id="smushing-layers">Smushing Layers</h4>

<p>I decided the best solution is to combine the layers which are less
likely to be a cache hit with other software. Picking the most
low level, fundamental paths and making them a separate layer means my
next image will most likely also share some of those layers too.</p>

<p>Ideally it would end up with at least glibc, and ncurses in separate
layers. Visually, it is hard to tell if either readline or bash-4.4p23
would be better served as an individual layer. One of them should be,
certainly.</p>

<h3 id="my-actual-solution">My actual solution</h3>

<p>My prioritization algorithm is a simple graph-based popularity
contest. The idea is to weight each node more heavily the deeper and
more references they have.</p>

<p>Starting with the dependency graph of Bash from before,</p>

<p><img src="/resources/2018-09-28-docker-images/bash-weighted-step0.svg" alt="" /></p>

<p>we first duplicate nodes in the graph so each node is only pointed to
once:</p>

<p><img src="/resources/2018-09-28-docker-images/bash-weighted-step1.svg" alt="" /></p>

<p>we then replace each leaf node with a counter, starting at 1:</p>

<p><img src="/resources/2018-09-28-docker-images/bash-weighted-step2.svg" alt="" /></p>

<p>each node whose only children are counters are then combined with
their children, and their children’s counters summed, then
incremented:</p>

<p><img src="/resources/2018-09-28-docker-images/bash-weighted-step3.svg" alt="" /></p>

<p>we then repeat the process:</p>

<p><img src="/resources/2018-09-28-docker-images/bash-weighted-step4.svg" alt="" /></p>

<p>we repeat this process until there is only one node:</p>

<p><img src="/resources/2018-09-28-docker-images/bash-weighted-step5.svg" alt="" /></p>

<p>and finally we sort the paths in each popularity bucket by name to
ensure the list is consistently generated to get the paths ordered by
cachability:</p>

<ul>
  <li>glibc-2.27: 10</li>
  <li>ncurses-6.1: 4</li>
  <li>bash-4.4-p23: 2</li>
  <li>readline-7.0p5: 2</li>
  <li>bash-interactive-4.4-p23: 1</li>
</ul>

<p>This solution has properly put foundational paths which are most
commonly referred to at the top, improving its chances of cache hit.
The algorithm has also put the likely-to-change application right at
the bottom in case the last layers need to be combined.</p>

<p>Let’s consider a much larger image. In this image, we set the maximum
number of layers to 120, but the image has 200 store paths. Under this
design the 119 most fundamental store paths will have their own
layers, and we store the remaining 81 paths together in the 120th
layer.</p>

<hr />

<p>With this new approach of automatically layering store paths I can now
generate images with very efficient caching between different
images.</p>

<p>For a practical example of a PHP application with a MySQL database.</p>

<p>First we build a MySQL image:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mysql.nix</span>
<span class="kd">let</span>
  <span class="nv">pkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="p">(</span><span class="kr">builtins</span><span class="o">.</span><span class="kr">fetchTarball</span> <span class="p">{</span>
    <span class="nv">url</span> <span class="o">=</span> <span class="s2">"https://github.com/grahamc/nixpkgs/archive/layered-docker-images.tar.gz"</span><span class="p">;</span>
    <span class="nv">sha256</span> <span class="o">=</span> <span class="s2">"05a3jjcqvcrylyy8gc79hlcp9ik9ljdbwf78hymi5b12zj2vyfh6"</span><span class="p">;</span>
  <span class="p">})</span> <span class="p">{};</span>
<span class="kn">in</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">dockerTools</span><span class="o">.</span><span class="nv">buildLayeredImage</span> <span class="p">{</span>
  <span class="nv">name</span> <span class="o">=</span> <span class="s2">"mysql"</span><span class="p">;</span>
  <span class="nv">tag</span> <span class="o">=</span> <span class="s2">"latest"</span><span class="p">;</span>
  <span class="nv">config</span><span class="o">.</span><span class="nv">Cmd</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="si">${</span><span class="nv">pkgs</span><span class="o">.</span><span class="nv">mysql</span><span class="si">}</span><span class="s2">/bin/mysqld"</span> <span class="p">];</span>
  <span class="nv">maxLayers</span> <span class="o">=</span> <span class="mi">120</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ nix-build ./mysql.nix
$ docker load &lt; ./result
</code></pre></div></div>

<p>Then we build a PHP image:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># php.nix</span>
<span class="kd">let</span>
  <span class="nv">pkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="p">(</span><span class="kr">builtins</span><span class="o">.</span><span class="kr">fetchTarball</span> <span class="p">{</span>
    <span class="nv">url</span> <span class="o">=</span> <span class="s2">"https://github.com/grahamc/nixpkgs/archive/layered-docker-images.tar.gz"</span><span class="p">;</span>
    <span class="nv">sha256</span> <span class="o">=</span> <span class="s2">"05a3jjcqvcrylyy8gc79hlcp9ik9ljdbwf78hymi5b12zj2vyfh6"</span><span class="p">;</span>
  <span class="p">})</span> <span class="p">{};</span>
<span class="kn">in</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">dockerTools</span><span class="o">.</span><span class="nv">buildLayeredImage</span> <span class="p">{</span>
  <span class="nv">name</span> <span class="o">=</span> <span class="s2">"grahamc/php"</span><span class="p">;</span>
  <span class="nv">tag</span> <span class="o">=</span> <span class="s2">"latest"</span><span class="p">;</span>
  <span class="nv">config</span><span class="o">.</span><span class="nv">Cmd</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="si">${</span><span class="nv">pkgs</span><span class="o">.</span><span class="nv">php</span><span class="si">}</span><span class="s2">/bin/php"</span> <span class="p">];</span>
  <span class="nv">maxLayers</span> <span class="o">=</span> <span class="mi">120</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ nix-build ./php.nix
$ docker load &lt; ./result
</code></pre></div></div>

<p>and export the two image layers:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker inspect mysql | jq -r '.[] | .RootFS.Layers | .[]' | sort &gt; mysql
$ docker inspect php | jq -r '.[] | .RootFS.Layers | .[]' | sort &gt; php
</code></pre></div></div>

<p>and look at this, the PHP and MySQL images share <strong>twenty</strong> layers:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ comm -1 -2 php mysql
sha256:0296e7b7d4b7d593fc533f06c5cc22f56c99c8ab0ed4301a6c7829ce8b18c6fa
sha256:1114fa18ba6be7e5db7728ae747a6bf0aab48fedf3ed9e95aff1f9ce51903698
sha256:181ca82fe4d920b46488d29b507d432dd121d9b2842cf8c720c0939e03e4d6a0
sha256:190b2db337109cb9692cd004aeed4b06fef0c10c700a9ba7939d73e697e3bbcc
sha256:1c4afa44a489abf7c7fe8fa642647a8a2786bf7581486e6a308e1078484784e6
sha256:424ad95540cb66adb9e16d768ccc7010923a9ca09dda1f56e4a08804c2de12e6
sha256:537b4796037a46b64fa39c42f642925704abbaab475e5c72a8fc15c258dc1e85
sha256:69bf797b6b6763938b98139814d8be884693806e0e5a50ac4fbbf11eb45f4f27
sha256:6c7f6a555dee6eebfc5497e84b1cacb4fff035e2101d8421768c0c2db54e8da2
sha256:7dcff293e9f411c63d6f1365795a89ac0058995de0f192ad9fb103ab56533ed3
sha256:86b1c4990ca1c80ad5dc0977fe37ab0f80e0f95e03fe79769b451d97e9f7a8f3
sha256:9d8278ca2542af2ff9cf2d8de439758f6b3bbb84bbe6b7a44edf8080b73d2949
sha256:a5c8632ba0135b465956281008a4f9c2263232d92c020b56e1d839aaa4b74834
sha256:b9fe88bf1364613ec01968480d7cb305d69e3de78bb4a56e3448298ffcc25139
sha256:c0c9c2eaa522dd31901c49a40577a68e3ae02cc75226a248813134046b299099
sha256:c2f4e79836f999ff389b82b8636b807c2baa5b702509b2991e651508519857d5
sha256:cdfb1dfb3ca2f8df9e87e5fa33b91a402a8b4ad0ede164dbdf4c25aded618ed3
sha256:ffd8e3d222cb85f642677642037a0e7886a565babdc0e229cc83147895a8ed2c
sha256:ffecd238fa95b110a1b5f71034b2bd358358758abb52fd098241200d94111979
</code></pre></div></div>

<p>Where before you wouldn’t bother trying to have your application and
database images share layers, with Nix the layer sharing is completely
automatic.</p>

<p>The automatic splitting and prioritization has improved image push and
fetch times by an order of magnitude. Having multiple images allows
Docker to request more than one at a time.</p>

<p><em>Thank you Target for having sponsored this work with
<a href="https://tweag.io/">Tweag</a> in
<a href="https://github.com/NixOS/nixpkgs/pull/47411">NixOS/nixpkgs#47411</a>.</em></p>

]]></description>
        <link>http://grahamc.com/blog/nix-and-layered-docker-images/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/an-epyc-nixos-build-farm/</guid>
        <title>an EPYC NixOS build farm</title>
        <pubDate>Thu, 02 Aug 2018 00:00:00 +0000</pubDate>
        <description><![CDATA[<h1 id="epyc-vs-m1xlargex86">EPYC vs m1.xlarge.x86</h1>

<p>Nix is a powerful package manager for Linux and other Unix systems
that makes package management reliable and reproducible. It provides
atomic upgrades and rollbacks, side-by-side installation of multiple
versions of a package, multi-user package management and easy setup of
build environments.</p>

<p>The Nix community has collected and curated build instructions
(<em>expressions</em>) for many thousands of packages in the Nix package
collection, Nixpkgs. The collection is a large GitHub repository,
which receives over a thousand pull requests each month. Some of these
changes can sometimes cause all of the packages to rebuild.</p>

<p>To test changes to Nixpkgs and release updates for Nix and NixOS, we
necessarily created our own build infrastructure. This allows us to
give better quality guarantees to our users.</p>

<p>The NixOS infrastructure team runs several types of servers: VMs on
AWS, bare metal, macOS systems, among others. We build thousands of
packages a day, sometimes reaching many tens of thousands per day.</p>

<p>Some of our builds depend on unique features like KVM which are only
available by using bare metal servers, and all of them benefit from
numerous, powerful cores.</p>

<p>For over a year now, Packet.net (where you can natively deploy NixOS
by the way!) has been generously providing bare metal hardware build
resources for the NixOS build farm, and together we were curious how
the new EPYC from AMD would compare to the hardware we were already
using.</p>

<p>For this benchmark we are comparing Packet’s <code class="language-plaintext highlighter-rouge">m1.xlarge.x86</code> against
Packet’s first EPYC machine, <code class="language-plaintext highlighter-rouge">c2.medium.x86</code>. Hydra already runs a
<code class="language-plaintext highlighter-rouge">m1.xlarge.x86</code> build machine, so the comparison will be helpful in
deciding if we should replace it with EPYC hardware.</p>

<p>AMD EPYC has the chance to reduce our hardware footprint, reduce our
need for our AWS scale-out, and improve our turnaround time for
time-sensitive security patches.</p>

<h2 id="system-comparison">System Comparison:</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th><a href="https://www.packet.net/bare-metal/servers/m1-xlarge/">m1.xlarge.x86</a></th>
      <th><a href="https://www.packet.net/bare-metal/servers/c2-medium-epyc/">c2.medium.x86</a> (EPYC)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><em>NixOS Version</em></td>
      <td><code class="language-plaintext highlighter-rouge">18.03.132610.49a6964a425</code></td>
      <td><code class="language-plaintext highlighter-rouge">18.03.132610.49a6964a425</code></td>
    </tr>
    <tr>
      <td><em>Cost per Hour</em></td>
      <td>$1.70/hr</td>
      <td>$1.00/hr</td>
    </tr>
    <tr>
      <td><em>CPU</em></td>
      <td>24 Physical Cores @ 2.2 GHz</td>
      <td>24 Physical Cores @ 2.2 GHz</td>
    </tr>
    <tr>
      <td> </td>
      <td>2 x <a href="https://ark.intel.com/products/91767/Intel-Xeon-Processor-E5-2650-v4-30M-Cache-2_20-GHz">Xeon® E5-2650 v4</a></td>
      <td>1 x <a href="https://www.amd.com/en/products/cpu/amd-epyc-7401p">AMD EPYC™ 7401P</a></td>
    </tr>
    <tr>
      <td><em>RAM</em></td>
      <td>256 GB of ECC RAM</td>
      <td>64 GB of ECC RAM</td>
    </tr>
  </tbody>
</table>

<h2 id="benchmark-methods">Benchmark Methods</h2>

<p>All of these tests were run on a fresh Packet.net server running NixOS
18.03 and building Nixpkgs revision <a href="https://github.com/NixOS/nixpkgs/tree/49a6964a4250d98644da61f24dcc11ee0b28c4f9"><code class="language-plaintext highlighter-rouge">49a6964a425</code></a>.</p>

<p>For each test, I ran each build five times on each machine with
<code class="language-plaintext highlighter-rouge">--check</code> which forces a local rebuild:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>checkM<span class="o">()</span> <span class="o">{</span>
  nix-build <span class="nb">.</span> <span class="nt">-A</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
  <span class="k">for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">seq </span>1 5<span class="si">)</span><span class="p">;</span> <span class="k">do
    </span><span class="nb">rm </span>result
    nix-collect-garbage
    /run/current-system/sw/bin/time <span class="nt">-ao</span> <span class="s2">"./time-</span><span class="nv">$1</span><span class="s2">"</span> <span class="se">\</span>
      nix-build <span class="nb">.</span> <span class="nt">-A</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="nt">--check</span>
  <span class="k">done</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="benchmark-results">Benchmark Results</h2>

<h3 id="kernel-builds">Kernel Builds</h3>

<p>NixOS builds a generously featureful kernel by default, and the build
can take some time. However, the compilation is easy to spread across
multiple cores. In what we will further see is a theme, the EPYC beat
the Intel CPU with by about five minutes, or about 35% speed-up.</p>

<p><code class="language-plaintext highlighter-rouge">nix-build '&lt;nixpkgs&gt;' -A linuxPackages.kernel</code></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Trial</th>
      <th style="text-align: right">m1.xlarge.x86 (seconds)</th>
      <th style="text-align: right">EPYC (seconds)</th>
      <th style="text-align: right">Speed-up</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><em>1</em></td>
      <td style="text-align: right">823.77</td>
      <td style="text-align: right">535.30</td>
      <td style="text-align: right">35.02%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>2</em></td>
      <td style="text-align: right">821.27</td>
      <td style="text-align: right">536.94</td>
      <td style="text-align: right">34.62%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>3</em></td>
      <td style="text-align: right">824.92</td>
      <td style="text-align: right">538.45</td>
      <td style="text-align: right">34.73%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>4</em></td>
      <td style="text-align: right">827.74</td>
      <td style="text-align: right">537.79</td>
      <td style="text-align: right">35.03%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>5</em></td>
      <td style="text-align: right">827.37</td>
      <td style="text-align: right">539.98</td>
      <td style="text-align: right">34.74%</td>
    </tr>
  </tbody>
</table>

<h3 id="nixos-tests">NixOS Tests</h3>

<p>The NixOS release process is novel in that package updates happen
automatically as an entire cohesive set. We of course test that
specific software compiles properly, but we are also able to perform
fully automatic integration tests. We test that the operating system
can boot, desktop environments work, and even server tests like
validating that our MySQL server replication is still working. These
tests happen automatically on every release and is a unique benefit to
the Nix build system being applied to an operating system.</p>

<p>These tests use QEMU and KVM, and spawn one or more virtual machines
running NixOS.</p>

<h4 id="plasma5-and-kde">Plasma5 and KDE</h4>

<p><a href="https://github.com/NixOS/nixpkgs/blob/49a6964a4250d98644da61f24dcc11ee0b28c4f9/nixos/tests/plasma5.nix">The Plasma5 test</a> looks like the following:</p>

<ol>
  <li>
    <p>First launches a NixOS VM configured to run the Plasma5 desktop
manager.</p>
  </li>
  <li>Uses Optical Character Recognition to wait until it sees the system
is ready for Alice to log in and then types in her password:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">waitForText</span><span class="p">(</span><span class="sx">qr/Alice Foobar/</span><span class="p">);</span>
<span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">sendChars</span><span class="p">("</span><span class="s2">foobar</span><span class="se">\n</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>Waits for the Desktop to be showing, which reliably indicates she
has finished logging in:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">waitForWindow</span><span class="p">("</span><span class="s2">^Desktop </span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>Launches Dolphin, Konsole, and Settings and waits for each window
to appear before continuing:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">execute</span><span class="p">("</span><span class="s2">su - alice -c 'dolphin &amp;'</span><span class="p">");</span>
<span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">waitForWindow</span><span class="p">("</span><span class="s2"> Dolphin</span><span class="p">");</span>
<span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">execute</span><span class="p">("</span><span class="s2">su - alice -c 'konsole &amp;'</span><span class="p">");</span>
<span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">waitForWindow</span><span class="p">("</span><span class="s2">Konsole</span><span class="p">");</span>
<span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">execute</span><span class="p">("</span><span class="s2">su - alice -c 'systemsettings5 &amp;'</span><span class="p">");</span>
<span class="nv">$machine</span><span class="o">-&gt;</span><span class="nv">waitForWindow</span><span class="p">("</span><span class="s2">Settings</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>If all of these work correctly, the VM shuts down and the tests
pass.</li>
</ol>

<p>Better than the kernel tests, we’re pushing a 40% improvement.</p>

<p><code class="language-plaintext highlighter-rouge">nix-build '&lt;nixpkgs/nixos/tests/plasma5.nix&gt;'</code></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Trial</th>
      <th style="text-align: right">m1.xlarge.x86 (seconds)</th>
      <th style="text-align: right">EPYC (seconds)</th>
      <th style="text-align: right">Speed-up</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><em>1</em></td>
      <td style="text-align: right">185.73</td>
      <td style="text-align: right">115.23</td>
      <td style="text-align: right">37.96%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>2</em></td>
      <td style="text-align: right">189.53</td>
      <td style="text-align: right">116.11</td>
      <td style="text-align: right">38.74%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>3</em></td>
      <td style="text-align: right">191.88</td>
      <td style="text-align: right">115.18</td>
      <td style="text-align: right">39.97%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>4</em></td>
      <td style="text-align: right">189.38</td>
      <td style="text-align: right">116.05</td>
      <td style="text-align: right">38.72%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>5</em></td>
      <td style="text-align: right">188.98</td>
      <td style="text-align: right">115.54</td>
      <td style="text-align: right">38.86%</td>
    </tr>
  </tbody>
</table>

<h4 id="mysql-replication">MySQL Replication</h4>

<p><a href="https://github.com/NixOS/nixpkgs/blob/49a6964a4250d98644da61f24dcc11ee0b28c4f9/nixos/tests/mysql-replication.nix">The MySQL replication test</a> launches a MySQL master and
two slaves, and runs some basic replication tests. NixOS allows you to
define replication as part of the regular configuration management, so
I will start by showing the machine configuration of a slave:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="p">{</span>
      <span class="nv">services</span><span class="o">.</span><span class="nv">mysql</span><span class="o">.</span><span class="nv">replication</span><span class="o">.</span><span class="nv">role</span> <span class="o">=</span> <span class="s2">"slave"</span><span class="p">;</span>
      <span class="nv">services</span><span class="o">.</span><span class="nv">mysql</span><span class="o">.</span><span class="nv">replication</span><span class="o">.</span><span class="nv">serverId</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
      <span class="nv">services</span><span class="o">.</span><span class="nv">mysql</span><span class="o">.</span><span class="nv">replication</span><span class="o">.</span><span class="nv">masterHost</span> <span class="o">=</span> <span class="s2">"master"</span><span class="p">;</span>
      <span class="nv">services</span><span class="o">.</span><span class="nv">mysql</span><span class="o">.</span><span class="nv">replication</span><span class="o">.</span><span class="nv">masterUser</span> <span class="o">=</span> <span class="s2">"replicate"</span><span class="p">;</span>
      <span class="nv">services</span><span class="o">.</span><span class="nv">mysql</span><span class="o">.</span><span class="nv">replication</span><span class="o">.</span><span class="nv">masterPassword</span> <span class="o">=</span> <span class="s2">"secret"</span><span class="p">;</span>
    <span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>This test starts by starting the <code class="language-plaintext highlighter-rouge">$master</code> and waiting for MySQL to
be healthy:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$master</span><span class="o">-&gt;</span><span class="nv">start</span><span class="p">;</span>
<span class="nv">$master</span><span class="o">-&gt;</span><span class="nv">waitForUnit</span><span class="p">("</span><span class="s2">mysql</span><span class="p">");</span>
<span class="nv">$master</span><span class="o">-&gt;</span><span class="nv">waitForOpenPort</span><span class="p">(</span><span class="mi">3306</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>Continues to start <code class="language-plaintext highlighter-rouge">$slave1</code> and <code class="language-plaintext highlighter-rouge">$slave2</code> and wait for them to be
up:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$slave1</span><span class="o">-&gt;</span><span class="nv">start</span><span class="p">;</span>
<span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">start</span><span class="p">;</span>
<span class="nv">$slave1</span><span class="o">-&gt;</span><span class="nv">waitForUnit</span><span class="p">("</span><span class="s2">mysql</span><span class="p">");</span>
<span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">waitForUnit</span><span class="p">("</span><span class="s2">mysql</span><span class="p">");</span>
<span class="nv">$slave1</span><span class="o">-&gt;</span><span class="nv">waitForOpenPort</span><span class="p">(</span><span class="mi">3306</span><span class="p">);</span>
<span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">waitForOpenPort</span><span class="p">(</span><span class="mi">3306</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>It then validates some of the scratch data loaded in to the
<code class="language-plaintext highlighter-rouge">$master</code> has replicated properly to <code class="language-plaintext highlighter-rouge">$slave2</code>:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">
     echo 'use testdb; select * from tests' </span><span class="se">\</span><span class="s2">
         | mysql -u root -N | grep 4
</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>Then shuts down <code class="language-plaintext highlighter-rouge">$slave2</code>:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">systemctl stop mysql</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>Writes some data to the <code class="language-plaintext highlighter-rouge">$master</code>:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$master</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">
     echo 'insert into testdb.tests values (123, 456);' </span><span class="se">\</span><span class="s2">
         | mysql -u root -N
</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>Starts <code class="language-plaintext highlighter-rouge">$slave2</code>, and verifies the queries properly replicated from
the <code class="language-plaintext highlighter-rouge">$master</code> to the slave:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">systemctl start mysql</span><span class="p">");</span>
<span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">waitForUnit</span><span class="p">("</span><span class="s2">mysql</span><span class="p">");</span>
<span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">waitForOpenPort</span><span class="p">(</span><span class="mi">3306</span><span class="p">);</span>
<span class="nv">$slave2</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">
     echo 'select * from testdb.tests where Id = 123;' </span><span class="se">\</span><span class="s2">
         | mysql -u root -N | grep 456
</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>Due to the multiple VM nature, and increased coordination between the
nodes, we saw a 30% increase.</p>

<p><code class="language-plaintext highlighter-rouge">nix-build '&lt;nixpkgs/nixos/tests/mysql-replication.nix&gt;'</code></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Trial</th>
      <th style="text-align: right">m1.xlarge.x86 (seconds)</th>
      <th style="text-align: right">EPYC (seconds)</th>
      <th style="text-align: right">Speed-up</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><em>1</em></td>
      <td style="text-align: right">42.32</td>
      <td style="text-align: right">29.94</td>
      <td style="text-align: right">29.25%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>2</em></td>
      <td style="text-align: right">43.46</td>
      <td style="text-align: right">29.48</td>
      <td style="text-align: right">32.17%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>3</em></td>
      <td style="text-align: right">42.43</td>
      <td style="text-align: right">29.83</td>
      <td style="text-align: right">29.70%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>4</em></td>
      <td style="text-align: right">43.27</td>
      <td style="text-align: right">29.66</td>
      <td style="text-align: right">31.45%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>5</em></td>
      <td style="text-align: right">42.07</td>
      <td style="text-align: right">29.37</td>
      <td style="text-align: right">30.19%</td>
    </tr>
  </tbody>
</table>

<h4 id="bittorrent">BitTorrent</h4>

<p><a href="https://github.com/NixOS/nixpkgs/blob/49a6964a4250d98644da61f24dcc11ee0b28c4f9/nixos/tests/bittorrent.nix">The BitTorrent test</a> follows the same pattern of starting
and stopping a NixOS VM, but this test takes it a step further and
tests with four VMs which talk to each other. I could do a whole post
<em>just</em> on this test, but in short:</p>

<ul>
  <li>a machine serving as the tracker, named <code class="language-plaintext highlighter-rouge">$tracker</code>.</li>
  <li>a client machine, <code class="language-plaintext highlighter-rouge">$client1</code></li>
  <li>another client machine, <code class="language-plaintext highlighter-rouge">$client2</code></li>
  <li>a router which facilitates some of the incredible things this test
is actually doing.</li>
</ul>

<p>I’m going to gloss over the details here, but:</p>

<ol>
  <li>The <code class="language-plaintext highlighter-rouge">$tracker</code> starts seeding a file:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$tracker</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">opentracker -p 6969 &amp;</span><span class="p">");</span>
<span class="nv">$tracker</span><span class="o">-&gt;</span><span class="nv">waitForOpenPort</span><span class="p">(</span><span class="mi">6969</span><span class="p">);</span>
<span class="k">my</span> <span class="nv">$pid</span> <span class="o">=</span> <span class="nv">$tracker</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">transmission-cli </span><span class="se">\</span><span class="s2">
     /tmp/test.torrent -M -w /tmp/data</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">$client1</code> fetches the file from the tracker:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$client1</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">transmission-cli </span><span class="se">\</span><span class="s2">
     http://tracker/test.torrent -w /tmp &amp;</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li>Kills the seeding process on tracker so now only <code class="language-plaintext highlighter-rouge">$client1</code> is able
to serve the file:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$tracker</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">kill -9 </span><span class="si">$pid</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">$client2</code> fetches the file from <code class="language-plaintext highlighter-rouge">$client1</code>:
    <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$client2</span><span class="o">-&gt;</span><span class="nv">succeed</span><span class="p">("</span><span class="s2">transmission-cli </span><span class="se">\</span><span class="s2">
     http://tracker/test.torrent -M -w /tmp &amp;</span><span class="p">");</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>If both <code class="language-plaintext highlighter-rouge">$client1</code> and <code class="language-plaintext highlighter-rouge">$client2</code> receive the file intact, the test
passes.</p>

<p>This test sees a much lower performance improvement, largely due to
the networked coordination across four VMs.</p>

<p><code class="language-plaintext highlighter-rouge">nix-build '&lt;nixpkgs/nixos/tests/bittorrent.nix&gt;'</code></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Trial</th>
      <th style="text-align: right">m1.xlarge.x86 (seconds)</th>
      <th style="text-align: right">EPYC (seconds)</th>
      <th style="text-align: right">Speed-up</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><em>1</em></td>
      <td style="text-align: right">54.22</td>
      <td style="text-align: right">45.37</td>
      <td style="text-align: right">16.32%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>2</em></td>
      <td style="text-align: right">54.40</td>
      <td style="text-align: right">45.51</td>
      <td style="text-align: right">16.34%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>3</em></td>
      <td style="text-align: right">54.57</td>
      <td style="text-align: right">45.34</td>
      <td style="text-align: right">16.91%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>4</em></td>
      <td style="text-align: right">55.31</td>
      <td style="text-align: right">45.32</td>
      <td style="text-align: right">18.06%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>5</em></td>
      <td style="text-align: right">56.07</td>
      <td style="text-align: right">45.45</td>
      <td style="text-align: right">18.94%</td>
    </tr>
  </tbody>
</table>

<p><em>The remarkable part I left out is Client1 uses UPnP to open a port of
the firewall of the router which Client2 uses to read from Client1.</em></p>

<h3 id="standard-environment">Standard Environment</h3>

<p>Our standard build environment, <code class="language-plaintext highlighter-rouge">stdenv</code>, is at the deepest part of
this tree and almost nothing can be built until after it is completed.
<code class="language-plaintext highlighter-rouge">stdenv</code> is like <code class="language-plaintext highlighter-rouge">build-essential</code> on Ubuntu.</p>

<p>This is an important part of the performance story for us: Nix builds
are represented by a tree, where Nix will schedule as many parallel
builds as possible as long as its parents are done building. Single
core performance is the primary factor impacting how long these builds
take. Shaving off even a few minutes means <a href="https://hydra.nixos.org/machines">our entire build
cluster</a> is able to get to share the work sooner.</p>

<p>For this reason, the stdenv test is the one exception to the
methodology. I wanted to test a full build from bootstrap to a working
standard build environment. To force this, I changed the very root
build causing everything “beneath” it to require a rebuild by applying
the following patch:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/pkgs/stdenv/linux/default.nix b/pkgs/stdenv/linux/default.nix
index 63b4c8ecc24..1cd27f216f9 100644
</span><span class="gd">--- a/pkgs/stdenv/linux/default.nix
</span><span class="gi">+++ b/pkgs/stdenv/linux/default.nix
</span><span class="p">@@ -37,6 +37,7 @@</span> let
<span class="err">
</span>   commonPreHook =
     ''
<span class="gi">+
</span>       export NIX_ENFORCE_PURITY="''${NIX_ENFORCE_PURITY-1}"
       export NIX_ENFORCE_NO_NATIVE="''${NIX_ENFORCE_NO_NATIVE-1}"
       ${if system == "x86_64-linux" then "NIX_LIB64_IN_SELF_RPATH=1" else ""}
</code></pre></div></div>

<p>The impact on build time here is stunning and makes an enormous
difference: almost a full 20 minutes shaved off the bootstrapping
time.</p>

<p><code class="language-plaintext highlighter-rouge">nix-build '&lt;nixpkgs&gt;' -A stdenv</code></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Trial</th>
      <th style="text-align: right">m1.xlarge.x86 (seconds)</th>
      <th style="text-align: right">EPYC (seconds)</th>
      <th style="text-align: right">Speed-up</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><em>1</em></td>
      <td style="text-align: right">2,984.24</td>
      <td style="text-align: right">1,803.40</td>
      <td style="text-align: right">39.57%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>2</em></td>
      <td style="text-align: right">2,976.10</td>
      <td style="text-align: right">1,808.97</td>
      <td style="text-align: right">39.22%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>3</em></td>
      <td style="text-align: right">2,990.66</td>
      <td style="text-align: right">1,808.21</td>
      <td style="text-align: right">39.54%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>4</em></td>
      <td style="text-align: right">2,999.36</td>
      <td style="text-align: right">1,808.30</td>
      <td style="text-align: right">39.71%</td>
    </tr>
    <tr>
      <td style="text-align: right"><em>5</em></td>
      <td style="text-align: right">2,988.46</td>
      <td style="text-align: right">1,818.84</td>
      <td style="text-align: right">39.14%</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>

<p>This EPYC machine has made a remarkable improvement in our build times
and is helping the NixOS community push timely security updates and
software updates to users and businesses alike. We look forward to
expanding our footprint to keep up with the incredible growth of the
Nix project.</p>

<p>Thank you to Packet.net for providing this hardware free of charge for
this test through their <a href="https://www.packet.net/epyc">EPYC Challenge</a>.</p>

<p><em>and thank you Gustav, Daiderd, Andi, and zimbatm for their help with
this article</em></p>

]]></description>
        <link>http://grahamc.com/blog/an-epyc-nixos-build-farm/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/cache-nixos-org-now-more-local/</guid>
        <title>cache.nixos.org, now more local!</title>
        <pubDate>Wed, 11 Jul 2018 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I’m delighted to be able to announce that users all around the world
will now have a great experience when fetching from the NixOS cache.</p>

<p>I heard several times from users in Hong Kong and Singapore that the
cache was “slow”, but I didn’t know it was <strong><em>this</em></strong> slow! After
working closely with a team of Nix users in Bangalore, I experienced
first-hand just how eye-wateringly slow it could be.</p>

<p>The NixOS cache is now being served from all of AWS CloudFront edge
locations, significantly reducing latency for users in Asia, Africa,
South America, and Oceania.</p>

<p>By expanding the cache’s distribution settings to include all of the
edge locations, performance has been substantially improved build
time:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
      <th style="text-align: left">Sydney</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>GHC<br />117.06 MiB</strong></td>
      <td><em>Before</em></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">178.491s</code></td>
    </tr>
    <tr>
      <td> </td>
      <td><em>After<br />Cold Cache</em></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">73.612s</code></td>
    </tr>
    <tr>
      <td> </td>
      <td><em>After<br />Hot Cache</em></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">15.707s</code></td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td style="text-align: left"> </td>
    </tr>
    <tr>
      <td><strong>Graphical ISO closure<br />1,660.95 MiB</strong></td>
      <td><em>Before</em></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">2,326.957s</code></td>
    </tr>
    <tr>
      <td> </td>
      <td><em>After<br />Cold Cache</em></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">376.014s</code></td>
    </tr>
    <tr>
      <td> </td>
      <td><em>After<br />Hot Cache</em></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">25.328s</code></td>
    </tr>
  </tbody>
</table>

<p>Experiments in Tokyo and Hong Kong produced similar results.</p>

<p>NixOS’s cache is stored in AWS S3, and distributed using AWS
CloudFront. This combination gives us the excellent durability
guarantees of S3 combined with the large geographical distribution of
CloudFront.</p>

<p>Until today, the NixOS cache was only served through edge nodes in the
United States, Canada, and Europe.</p>

<p>A big thank-you to Amine Chikhaoui, and Eelco Dolstra for their help
in researching this change and turning on such a massive improvement.</p>
]]></description>
        <link>http://grahamc.com/blog/cache-nixos-org-now-more-local/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/nixos-system-version-prometheus/</guid>
        <title>Prometheus and the NixOS System Version</title>
        <pubDate>Sun, 04 Feb 2018 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Use the Prometheus Node Exporter Textfile collector to help
correlate changes in your application’s behavior with NixOS
deployments.</p>

<p>First, configure Prometheus’s NodeExporter to enable Textfile
collection:</p>

<figure class="highlight"><pre><code class="language-nix" data-lang="nix"><span class="nv">services</span><span class="o">.</span><span class="nv">prometheus</span><span class="o">.</span><span class="nv">nodeExporter</span> <span class="o">=</span> <span class="p">{</span>
  <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  <span class="nv">enabledCollectors</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s2">"textfile"</span>
  <span class="p">];</span>
  <span class="nv">extraFlags</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s2">"--collector.textfile.directory=/var/lib/prometheus-node-exporter-text-files"</span>
  <span class="p">];</span>
<span class="p">};</span></code></pre></figure>

<p>Second, populate the textfile directory with the current system
version on every boot and deployment:</p>

<figure class="highlight"><pre><code class="language-nix" data-lang="nix"><span class="nv">system</span><span class="o">.</span><span class="nv">activationScripts</span><span class="o">.</span><span class="nv">node-exporter-system-version</span> <span class="o">=</span> <span class="s2">''</span><span class="err">
</span><span class="s2">  mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files</span><span class="err">
</span><span class="s2">  (</span><span class="err">
</span><span class="s2">    cd /var/lib/prometheus-node-exporter-text-files</span><span class="err">
</span><span class="s2">    (</span><span class="err">
</span><span class="s2">      echo -n "system_version ";</span><span class="err">
</span><span class="s2">      readlink /nix/var/nix/profiles/system | cut -d- -f2</span><span class="err">
</span><span class="s2">    ) &gt; system-version.prom.next</span><span class="err">
</span><span class="s2">    mv system-version.prom.next system-version.prom</span><span class="err">
</span><span class="s2">  )</span><span class="err">
</span><span class="s2">''</span><span class="p">;</span></code></pre></figure>

<p>Then, configure Grafana to use the <code class="language-plaintext highlighter-rouge">system_version</code> as an Annotation
with the following query:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>changes(system_version[5m])
</code></pre></div></div>
]]></description>
        <link>http://grahamc.com/blog/nixos-system-version-prometheus/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/nixos-on-dell-9560/</guid>
        <title>NixOS on a Dell 9560</title>
        <pubDate>Mon, 15 May 2017 00:00:00 +0000</pubDate>
        <description><![CDATA[<h1 id="documents">Documents</h1>

<p>Repair manual: <a href="http://topics-cdn.dell.com/pdf/xps-15-9560-laptop_setup%20guide_en-us.pdf">http://topics-cdn.dell.com/pdf/xps-15-9560-laptop_setup%20guide_en-us.pdf</a></p>

<p>I have:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Product Name: XPS 15 9560
System BIOS: 1.0.3
ePSA: Build 4304.17 UEFI ROM
</code></pre></div></div>

<h2 id="initial-setup">Initial Setup</h2>
<h3 id="make-a-usb-recovery-disk-for-windows">Make a USB recovery disk for windows</h3>

<p>You need a 16gb flash drive, and it will use the whole drive. Search
Windows for “recovery” and it should recommend “Create a recovery
drive” or something.</p>

<p>Make a note on the drive, store it for later. Just in case.</p>

<p>Also write the installer image to a USB disk ask described in the
instructions:
<a href="https://nixos.org/nixos/manual/index.html#sec-uefi-installation">https://nixos.org/nixos/manual/index.html#sec-uefi-installation</a></p>

<p>Plug in your USB disk before rebooting to the BIOS.</p>

<h3 id="reboot-to-the-bios">Reboot to the BIOS</h3>

<p>Pressing F2 when the DELL logo appears on the screen (repair manual,
page 96) takes you to the BIOS.</p>

<p>Pressing F2 <em>before</em> the DELL logo appears enters you in to a
Diagnostic Boot.</p>

<p>Note: You don’t need to press “Fn” with “F2” to enter setup.</p>

<p>You can navigate the BIOS with the arrow keys, or mouse, or touch
screen.</p>

<h4 id="disable-secure-boot">Disable Secure Boot</h4>

<p>Navigate to Secure boot -&gt; Secure Boot Enable, select Disabled.</p>

<p>In General -&gt; Boot Sequence, you should see in the top right “Windows
Boot Manager” and an entry for your USB drive, “UEFI: …”. Select the
Windows Boot Manager by tapping on it or with the mouse, then using
the arrow icons to right move it to the bottom of the priority list.</p>

<p>When I did this, I also inserted my Windows Recovery USB and made it
the highest priority. This way, I can check the disk is good, reboot,
unplug, and move on to the NixOS without going back in to the BIOS.</p>

<p>This seemed to only make the change for a single boot. I also always
saved these as User Custom Settings or whatever the option is. I’m not
sure what that means, but I did it.</p>

<h4 id="disable-raid-mode">Disable RAID mode</h4>

<p>System Configuration -&gt; SATA Operation -&gt; Select AHCI.</p>

<p>In RAID mode, Linux doesn’t seem to pick them up. I’m not sure why it
is in RAID mode to begin with. If you want to dig in to this, the
hardware reported here as Intel 82801 SATA in RAID mode.</p>

<h4 id="optional-enable-usb-wake-support">Optional: Enable USB Wake Support</h4>

<p>Power Management -&gt; USB Wake Support -&gt; Enable USB Wake Support</p>

<h4 id="interesting-features">Interesting Features</h4>

<ul>
  <li>System Configuration -&gt; USB PowerShare</li>
  <li>Security -&gt; Various password settings (including Strong Password
and Password Bypass)</li>
  <li>Security -&gt; TPM 2.0 Security</li>
  <li>Intel Software Guard Extensions</li>
  <li>C-States Control?</li>
  <li>Turboboost?</li>
  <li>MEBx Hotkey</li>
</ul>

<p>According to Security -&gt; OROM Keyboard Access, you can press:</p>

<ul>
  <li>Ctrl-1 to access Intel RAID config</li>
  <li>Crll-P/F12 to access Intel Management Engine BIOS Extension</li>
</ul>

<h2 id="reboot-in-to-the-nixos-installer">Reboot in to the NixOS Installer</h2>

<p>I reboot and there it is, loaded, the beautiful (lol) systemd-boot.
The text is very small. God help our eyes.</p>

<p>At the boot menu, I selected the default, “NixOS Live CD”. It worked
perfectly. I also tried the “NixOS Live CD (with nomodeset)” and it
seemed to work just the same. I continued on with “NixOS Live CD”.</p>

<h4 id="resolution">Resolution….</h4>

<p>I tried pressing <code class="language-plaintext highlighter-rouge">e</code> to edit boot options and set the screen to a
lower resolution with <code class="language-plaintext highlighter-rouge">vga</code> and <code class="language-plaintext highlighter-rouge">video</code>, but none of them worked. One
point for debugging is I see in the journal:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fb0: EFI VGA frame buffer device
</code></pre></div></div>

<p>and if I looked at <code class="language-plaintext highlighter-rouge">/sys/class/graphics/fb0/modes</code> I only saw one mode:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3840x2160p-87
</code></pre></div></div>

<p>Windows reports a resolution of 3840x2160, but scaled 250%.</p>

<p>After you boot, you can type <code class="language-plaintext highlighter-rouge">setfont latarcyrheb-sun32</code> and get a
slightly larger font. (Found:
<a href="https://wiki.archlinux.org/index.php/HiDPI#Linux_console">https://wiki.archlinux.org/index.php/HiDPI#Linux_console</a>)</p>

<h4 id="wifi">Wifi</h4>

<p>Works out of the box! Yay! Uses an Atheros driver (model QCA6174)</p>

<p>can be setup with:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wpa_supplicant -D nl80211,ext -i wlp2s0 -c &lt;(wpa_passphrase YourWififNetwork "YourWifiPassword")
</code></pre></div></div>

<p>After connecting, Fn-Alt-F2 to change to TTY2 for the rest of the
work. (Fix the font there, too.)</p>

<p>This was extremely helpful: <a href="https://github.com/NixOS/nixpkgs/issues/13342#issuecomment-301451545">https://github.com/NixOS/nixpkgs/issues/13342#issuecomment-301451545</a></p>

<h4 id="disks">Disks</h4>

<p>If you don’t have devices at <code class="language-plaintext highlighter-rouge">/dev/nvme_*</code> you forgot to turn off RAID
mode, or you’re on your own…</p>

<p>You’ll want to use <code class="language-plaintext highlighter-rouge">gdisk</code> on <code class="language-plaintext highlighter-rouge">/dev/nvme0n1</code>, I found this via <code class="language-plaintext highlighter-rouge">lsblk</code>,
as according to the UEFI instructions in the manual.</p>

<p>I then deleted partitions 2 through 6, leaving only 500M EFI system
partition, and created partitions so it looked like:</p>

<table>
  <thead>
    <tr>
      <th>Partition</th>
      <th>Size</th>
      <th>Code</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>500 MiB</td>
      <td><code class="language-plaintext highlighter-rouge">EF00</code></td>
      <td>EFI partition</td>
    </tr>
    <tr>
      <td>2</td>
      <td>3 MiB</td>
      <td><code class="language-plaintext highlighter-rouge">8300</code></td>
      <td>cryptsetup luks key</td>
    </tr>
    <tr>
      <td>3</td>
      <td>16 GiB</td>
      <td><code class="language-plaintext highlighter-rouge">8300</code></td>
      <td>swap space (hibernation)</td>
    </tr>
    <tr>
      <td>4</td>
      <td>remaining (460.4 GiB)</td>
      <td><code class="language-plaintext highlighter-rouge">8300</code></td>
      <td>root filesystem</td>
    </tr>
  </tbody>
</table>

<p>Note I use <code class="language-plaintext highlighter-rouge">8300</code> as the code because they’re all encrypted. Calling
the swap partition swap, systemd will try to automatically use it.</p>

<p>The cryptsetup luks key partition is 3 MiB so that it can fit the default
<a href="https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions">2 MiB LUKS header</a>
as well as the key.</p>

<p>Then:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Create an encrypted disk to hold our key, the key to this drive
# is what you'll type in to unlock the rest of your drives... so,
# remember it:
$ cryptsetup luksFormat /dev/nvme0n1p2
$ cryptsetup luksOpen /dev/nvme0n1p2 cryptkey

# Fill our key disk with random data, wihch will be our key:
$ dd if=/dev/urandom of=/dev/mapper/cryptkey bs=1024 count=14000

# Use the encrypted key to create our encrypted swap:
$ cryptsetup luksFormat --key-file=/dev/mapper/cryptkey /dev/nvme0n1p3

# Create an encrypted root with a key you can remember.
$ cryptsetup luksFormat /dev/nvme0n1p4
# Now add the cryptkey as a decryption key to the root partition, this
# way you can only decrypt the cryptkey on startup, and use the
# cryptkey to decrypt the root.
#
# The first human-rememberable key we added is just in case.
$ cryptsetup luksAddKey /dev/nvme0n1p4 /dev/mapper/cryptkey

# Now we open the swap and the root and make some filesystems.
$ cryptsetup luksOpen --key-file=/dev/mapper/cryptkey /dev/nvme0n1p3 cryptswap
$ mkswap /dev/mapper/cryptswap

$ cryptsetup luksOpen --key-file=/dev/mapper/cryptkey /dev/nvme0n1p4 cryptroot
$ mkfs.ext4 /dev/mapper/cryptroot

# and rebuild the boot partition:
$ mkfs.vfat /dev/nvme0n1p1
</code></pre></div></div>

<p>Then for a not fun bit, matching entries in <code class="language-plaintext highlighter-rouge">/dev/disk/by-uuid/</code> to
the partitions we want to mount where. Running <code class="language-plaintext highlighter-rouge">ls -l
/dev/disk/by-uuid/</code> shows which devices have which UUIDs. To determine
what <code class="language-plaintext highlighter-rouge">dm-1</code> and <code class="language-plaintext highlighter-rouge">dm2</code>, I ran <code class="language-plaintext highlighter-rouge">ls -la /dev/mapper</code>:</p>

<table>
  <thead>
    <tr>
      <th>name</th>
      <th>symlink to</th>
      <th>note</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1234-5678</code></td>
      <td><code class="language-plaintext highlighter-rouge">sda2</code></td>
      <td>installer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">1970-01-01-00-00-01-00</code></td>
      <td><code class="language-plaintext highlighter-rouge">sda1</code></td>
      <td>installer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AAAA-AAAA</code></td>
      <td><code class="language-plaintext highlighter-rouge">nvme0n1p1</code></td>
      <td>/boot</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB</code></td>
      <td><code class="language-plaintext highlighter-rouge">nvme0n1p2</code></td>
      <td>encrypted cryptkey</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC</code></td>
      <td><code class="language-plaintext highlighter-rouge">nvme0n1p3</code></td>
      <td>encrypted cryptswap</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD</code></td>
      <td><code class="language-plaintext highlighter-rouge">nvme0n1p4</code></td>
      <td>encrypted cryptroot</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE</code></td>
      <td><code class="language-plaintext highlighter-rouge">dm-1</code></td>
      <td>decrypted cryptswap</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF</code></td>
      <td><code class="language-plaintext highlighter-rouge">dm-2</code></td>
      <td>decrypted cryptroot</td>
    </tr>
  </tbody>
</table>

<p>Note I do have a <code class="language-plaintext highlighter-rouge">dm-0</code> for <code class="language-plaintext highlighter-rouge">cryptkey</code>, but no UUID but we won’t need
it. I substituted the actual hash with <code class="language-plaintext highlighter-rouge">A</code> s <code class="language-plaintext highlighter-rouge">B</code> s <code class="language-plaintext highlighter-rouge">C</code> s <code class="language-plaintext highlighter-rouge">D</code> s <code class="language-plaintext highlighter-rouge">E</code> s and
<code class="language-plaintext highlighter-rouge">F</code> s in order to make the mount commands easier.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Enable swap using the decrypted cryptswap:
$ swapon /dev/disk/by-uuid/EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE

# Mount the decrypted cryptroot to /mnt
$ mount /dev/disk/by-uuid/FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF /mnt

# Setup and mount the boot partition
$ mkdir /mnt/boot
$ mount /dev/disk/by-uuid/AAAA-AAAA /mnt/boot
</code></pre></div></div>

<h3 id="initial-configuration">Initial Configuration</h3>

<p>Run <code class="language-plaintext highlighter-rouge">nixos-generate-config --root /mnt</code></p>

<h4 id="hardware-configurationnix-changes"><code class="language-plaintext highlighter-rouge">hardware-configuration.nix</code> changes</h4>

<p>I had to edit the <code class="language-plaintext highlighter-rouge">hardware-configuration.nix</code> to setup the luks
configuration. I did this with <code class="language-plaintext highlighter-rouge">nix-shell -p emacs</code>, deleted the
<code class="language-plaintext highlighter-rouge">boot.initrd.luks.devices</code> line, and added:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="c"># !!! cryptkey must be done first, and the list seems to be</span>
  <span class="c"># alphabetically sorted, so take care that cryptroot / cryptswap,</span>
  <span class="c"># whatever you name them, come after cryptkey.</span>
  <span class="nv">boot</span><span class="o">.</span><span class="nv">initrd</span><span class="o">.</span><span class="nv">luks</span><span class="o">.</span><span class="nv">devices</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">cryptkey</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nv">device</span> <span class="o">=</span> <span class="s2">"/dev/disk/by-uuid/BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB"</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="nv">cryptroot</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nv">device</span> <span class="o">=</span> <span class="s2">"/dev/disk/by-uuid/DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD"</span><span class="p">;</span>
      <span class="nv">keyFile</span> <span class="o">=</span> <span class="s2">"/dev/mapper/cryptkey"</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="nv">cryptswap</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nv">device</span> <span class="o">=</span> <span class="s2">"/dev/disk/by-uuid/CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC"</span><span class="p">;</span>
      <span class="nv">keyFile</span> <span class="o">=</span> <span class="s2">"/dev/mapper/cryptkey"</span><span class="p">;</span>
    <span class="p">};</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It should already be correct, but check that:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">swapDevices</code> refers to
<code class="language-plaintext highlighter-rouge">/dev/disk/by-uuid/EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE</code></li>
  <li><code class="language-plaintext highlighter-rouge">fileSystems."/boot".device</code> refers to
<code class="language-plaintext highlighter-rouge">/dev/disk/by-uuid/AAAA-AAAA</code></li>
  <li><code class="language-plaintext highlighter-rouge">fileSystems."/".device</code> refers to
<code class="language-plaintext highlighter-rouge">/dev/disk/by-uuid/FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF</code></li>
</ol>

<h4 id="configurationnix-changes"><code class="language-plaintext highlighter-rouge">configuration.nix</code> changes</h4>

<p>The generated <code class="language-plaintext highlighter-rouge">configuration.nix</code> seemed correct, too, but I made some
changes:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="c"># I set the boot.kernelPackages to linuxPackages_latest just out of</span>
  <span class="c"># habit, since I'm a power-user I'd rather detect problems before</span>
  <span class="c"># other users.</span>
  <span class="nv">boot</span><span class="o">.</span><span class="nv">kernelPackages</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">linuxPackages_latest</span><span class="p">;</span>

  <span class="c"># I like networkmanager over wpa_supplicant, and with networkmanager</span>
  <span class="c"># you don't need to enable networking.wireless. You _do_ need to</span>
  <span class="c"># enable one of them, or you'll not have wifi when you boot back up.</span>
  <span class="nv">networking</span><span class="o">.</span><span class="nv">networkmanager</span><span class="o">.</span><span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

  <span class="nv">i18n</span><span class="o">.</span><span class="nv">consoleFont</span> <span class="o">=</span> <span class="s2">"latarcyrheb-sun32"</span><span class="p">;</span>

  <span class="c"># See the cpufreq section below</span>
  <span class="nv">powerManagement</span><span class="o">.</span><span class="nv">cpuFreqGovernor</span> <span class="o">=</span> <span class="s2">"powersave"</span><span class="p">;</span>

  <span class="nv">services</span><span class="o">.</span><span class="nv">xserver</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="c"># Note I didn't set autorun to be true until I figured out the</span>
    <span class="c"># monitorSection / went through the "X Server Resolution" process</span>
    <span class="c"># below, because the xserver display was much too small. I set it</span>
    <span class="c"># to true after I had sorted out the DPI.</span>
    <span class="nv">autorun</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

    <span class="c"># libinput performs better for me than synaptics:</span>
    <span class="nv">libinput</span><span class="o">.</span><span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

    <span class="c"># Just my personal preference:</span>
    <span class="nv">displayManager</span><span class="o">.</span><span class="nv">lightdm</span><span class="o">.</span><span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="nv">windowManager</span><span class="o">.</span><span class="nv">i3</span><span class="o">.</span><span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

    <span class="nv">monitorSection</span> <span class="o">=</span> <span class="s2">''</span><span class="err">
</span><span class="s2">      DisplaySize 406 228</span><span class="err">
</span><span class="s2">    ''</span><span class="p">;</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I did not set <code class="language-plaintext highlighter-rouge">users.mutableUsers</code> to <code class="language-plaintext highlighter-rouge">false</code> yet, or create my own
user. I usually do this once I get to a GUI so I can set
<code class="language-plaintext highlighter-rouge">hashedPassword</code>.</p>

<h4 id="x-server-resolution">X Server Resolution</h4>

<p>Note that when testing this, I would:</p>

<ol>
  <li>run <code class="language-plaintext highlighter-rouge">systemctl start display-manager</code> to start it</li>
  <li>Press escape to exit the i3 configurator</li>
  <li>press Alt-Enter to get a terminal</li>
  <li>run <code class="language-plaintext highlighter-rouge">systemctl stop display-manager</code> to stop X</li>
  <li>type Fn-Alt-F1 to get back to my terminal</li>
</ol>

<p><code class="language-plaintext highlighter-rouge">xdpyinfo | grep -B2 resolution</code> (installed with <code class="language-plaintext highlighter-rouge">nix-shell -p
xorg.xdpyinfo</code>) revealed:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>screen #0:
 dimensions:  3840x2160 pixels (1016x571 millimeters)
 resolution:  96x96 dots per inch
</code></pre></div></div>

<h5 id="successfully-raised-the-resolution">Successfully raised the resolution:</h5>

<p>I took the 1016x571 and divided by 2.5, based on the 250% scaling I
saw in the Windows settings, and using
<a href="https://wiki.archlinux.org/index.php/Xorg#Display_size_and_DPI">https://wiki.archlinux.org/index.php/Xorg#Display_size_and_DPI</a>, came
up with:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">services</span><span class="o">.</span><span class="nv">xserver</span><span class="o">.</span><span class="nv">monitorSection</span> <span class="o">=</span> <span class="s2">''</span><span class="err">
</span><span class="s2">    DisplaySize 406 228</span><span class="err">
</span><span class="s2">  ''</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>however when I started a terminal in i3, it was still tiny. Adding
<code class="language-plaintext highlighter-rouge">terminator</code> to <code class="language-plaintext highlighter-rouge">systemPackages</code> seemed to scale correctly. Note: on
my other High-DPI computer, I never fixed xterm and it never seemed to
matter.</p>

<h5 id="failed-to-fix-the-resolution">Failed to fix the resolution:</h5>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">services</span><span class="o">.</span><span class="nv">xserver</span><span class="o">.</span><span class="nv">screenSection</span> <span class="o">=</span> <span class="s2">''</span><span class="err">
</span><span class="s2">    Option "DPI" "240 x 240"</span><span class="err">
</span><span class="s2">  ''</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h5 id="fixed-the-resolution-but-was-super-fuzzy">Fixed the resolution, but was super fuzzy:</h5>

<p>On startup, running <code class="language-plaintext highlighter-rouge">xrandr --output eDP1 --scale 0.4x0.4</code> (0.4 being
1 divided by 2.5)</p>
<h3 id="install">Install…</h3>

<p>NOTE: If you use <code class="language-plaintext highlighter-rouge">nix-shell</code> you must not run <code class="language-plaintext highlighter-rouge">nixos-install</code> inside
the <code class="language-plaintext highlighter-rouge">nix-shell</code>!</p>

<p>I ran <code class="language-plaintext highlighter-rouge">nixos-install</code>, rebooted, and it worked like a charm.</p>

<p>Boots fine!</p>

<h3 id="hardware-check">Hardware Check</h3>

<h4 id="battery-sensor-worked-out-of-the-box">Battery sensor worked out of the box</h4>
<h4 id="webcam-looks-up-your-nose-but-worked-fine-out-of-the-box">Webcam looks up your nose, but worked fine out of the box.</h4>
<h4 id="backlight-intel-works-out-of-the-box">Backlight (intel) works out of the box</h4>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat /sys/class/backlight/intel_backlight/max_brightness
187

$ echo "100" | sudo tee /sys/class/backlight/intel_backlight/brightness

$ nix-shell -p xorg.xbacklight --run "xbacklight -set 50"
</code></pre></div></div>

<p>See: <a href="https://wiki.archlinux.org/index.php/Backlight">https://wiki.archlinux.org/index.php/Backlight</a></p>

<h4 id="audio-works-out-of-the-box-including-headphone-switching">Audio works out of the box, including headphone switching</h4>

<p>Volume control: <code class="language-plaintext highlighter-rouge">alsamixer</code> works out of the box, using Master /
Headphone / Speaker mixers.</p>

<h4 id="suspend">Suspend</h4>

<p><code class="language-plaintext highlighter-rouge">systemctl suspend</code> suspects correctly, and pressing the mouse button
resumes correctly.</p>

<h4 id="hibernate">Hibernate</h4>
<p><code class="language-plaintext highlighter-rouge">systemctl hibernate</code> great if you have swap.</p>

<h4 id="trackpad">Trackpad</h4>

<p>Using <code class="language-plaintext highlighter-rouge">services.xserver.libinput.enable = true;</code> fixes all these
problems, and the touch screen continue to work. Right click by
clicking in the bottom right corner.</p>

<p>Notes from before:</p>

<p>It works, but it seems to be in absolute positioning mode, and right
click seems to not work.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psmouse serio1: synaptics: device claims to have extended capability 0x0c, but I'm not able to read it.
psmouse serio1: synaptics: Unable to initialize device.
</code></pre></div></div>

<p>Later:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psmouse serio1: Failed to enable mouse on isa0060/serio1
</code></pre></div></div>

<h4 id="acpi-firmware-bug-bios-_osilinux-query-ignored">=ACPI: [Firmware Bug]: BIOS _OSI(Linux) query ignored=</h4>
<p>Not an issue:
<a href="https://askubuntu.com/questions/175793/what-does-the-following-dmesg-output-means">https://askubuntu.com/questions/175793/what-does-the-following-dmesg-output-means</a></p>

<h4 id="cpupower">cpupower</h4>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cpupower: Setting cpu: 0
cpupower: Error setting new values.
</code></pre></div></div>

<p>Add: <code class="language-plaintext highlighter-rouge">powerManagement.cpuFreqGovernor = "powersave";</code> to your
configuration. See: <a href="https://github.com/NixOS/nixpkgs/issues/9611">https://github.com/NixOS/nixpkgs/issues/9611</a></p>

<h3 id="bios-upgrades-without-windows">BIOS Upgrades Without Windows</h3>

<ol>
  <li>Make sure you’re on AC power. NOT the optional battery pack! It
will not boot to USB on the battery pack.</li>
  <li><code class="language-plaintext highlighter-rouge">nix-shell -p gparted -p unetbootin</code></li>
  <li>run <code class="language-plaintext highlighter-rouge">sudo gparted</code> a format your USB disk to have one partition,
FAT32, with the boot flag. After, right click and Format the
partition as fat32.</li>
  <li>Mount your USB disk (mine was at <code class="language-plaintext highlighter-rouge">/dev/sda1</code>) to <code class="language-plaintext highlighter-rouge">/mnt</code></li>
  <li>Run <code class="language-plaintext highlighter-rouge">sudo unetbootin</code></li>
  <li>Select FreeDOS 1.0 from the menu</li>
  <li>Make sure the <code class="language-plaintext highlighter-rouge">USB Drive</code> <code class="language-plaintext highlighter-rouge">Type</code> is selected, and select the USB
drive (again, mine was <code class="language-plaintext highlighter-rouge">/dev/sda1</code>) Then press OK</li>
  <li>Download your BIOS update from Dell, and copy it to <code class="language-plaintext highlighter-rouge">/mnt/</code>.</li>
  <li>Reboot to the BIOS setup</li>
  <li>under Boot Options change <code class="language-plaintext highlighter-rouge">UEFI</code> to <code class="language-plaintext highlighter-rouge">Legacy</code> and make sure the
boot devices above included at least USB.</li>
  <li><strong>Prepare to stick around for the next 4 steps! Don’t look away!
FreeDOS’s default option is install to the hard disk! It may be
fine, but I’ve never tried it.</strong></li>
  <li>Save and quit with the USB device plugged in.</li>
  <li>Select the only boot option</li>
  <li>Then select the “safe mode” option that says it won’t load
drivers.</li>
  <li>Type <code class="language-plaintext highlighter-rouge">C:&lt;enter&gt;</code> then <code class="language-plaintext highlighter-rouge">dir&lt;enter&gt;</code> and type <code class="language-plaintext highlighter-rouge">XPS&lt;tab&gt;</code> to
auto-select the XPS BIOS update and press Enter.</li>
  <li>The update will prompt for <code class="language-plaintext highlighter-rouge">y</code> a few times. Once it is done, enter
the BIOS again.</li>
  <li>Verify the update took place</li>
  <li>Disable the legacy boot, switching back to UEFI.</li>
</ol>

<h3 id="things-to-look-in-to">Things to look in to…</h3>
<h4 id="touch-screen">Touch screen</h4>
<ul>
  <li>Touch screen works out of the box (WHAT!)</li>
  <li>doesn’t scroll?</li>
  <li>any touch is a click</li>
</ul>

<p>In <code class="language-plaintext highlighter-rouge">/proc/bus/input/devices</code> the touchscreen is labeled:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I: Bus=0003 Vendor=04f3 Product=24a0 Version=0110
N: Name="ELAN Touchscreen"
P: Phys=usb-0000:00:14.0-9/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-9/1-9:1.0/0003:04F3:24A0.0001/input/input11
U: Uniq=
H: Handlers=event6 mouse0
B: PROP=2
B: EV=2
B: KEY=400 0 0 0 0 0
B: ABS=3273800000000003 (may be wrong number of 0's?)
</code></pre></div></div>

<p>Running <code class="language-plaintext highlighter-rouge">cat /dev/input/event6</code> revealed lots of output when I touched
the screen.</p>

<p>Under <code class="language-plaintext highlighter-rouge">xinput</code> it is <code class="language-plaintext highlighter-rouge">id=12</code>. <code class="language-plaintext highlighter-rouge">xinput list-props 12</code> revealed more
information.</p>

<p>See:</p>

<ul>
  <li><a href="https://wiki.archlinux.org/index.php/Touchscreen">https://wiki.archlinux.org/index.php/Touchscreen</a></li>
  <li><a href="https://wiki.archlinux.org/index.php/Libinput">https://wiki.archlinux.org/index.php/Libinput</a></li>
  <li><a href="https://askubuntu.com/questions/863563/libinput-and-touchscreen-support">https://askubuntu.com/questions/863563/libinput-and-touchscreen-support</a></li>
</ul>

<h4 id="vga-switcheroo">VGA Switcheroo</h4>

<p>Unable to enable Bumblebee, because I’m using the latest linux kernel
and the nvidia driver doesn’t compile with it yet.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VGA Switcheroo: detected Optimus DSM method \_SB_.PCI0.PEG0.PEGP handle
nouveau: detected PR support, will not use DSM
nouveau: 0000:01:00.0: enabling device (0006 -&gt; 0007)
nouveau: 0000:01:00.0: unknown chipset (137000a1)
nouveau: probe of 0000:01:00.0 failed with error -12
</code></pre></div></div>

<h4 id="wifi-1">Wifi</h4>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>athk10k_pci 0000:02:00.0: Direct firmware load for athk10k/pre-cal-pci-0000:02:00.0.bin failed with error -2
athk10k_pci 0000:02:00.0: Direct firmware load for athk10k/cal-pci-0000:02:00.0.bin failed with error -2
athk10k_pci 0000:02:00.0: Direct firmware load for ath10k/QCA6174/hw3.0/firmware-5.bin failed with error -2
athk10k_pci 0000:02:00.0: could not fetch firmware file 'ath10k/QCA6174/hw3.0/firmware-5.bin': -2
</code></pre></div></div>

<h4 id="warnings-pre-crypt-setup">Warnings pre crypt-setup</h4>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ACPI Error: [\_SB_.PCIO.XHC_.RHUB.HS11] Namespace lookup failure, AE_NOT_FOUND (20160831/dswload-210)
ACPI Exception: AE_NOT_FOUND, During name lookup/catalog (20160831/psobject-227)
ACPI Exception: AE_NOT_FOUND, (SSDT:xh_rvp11) while loading table (20160831/tbxfload-228)
ACPI Error: 1 table load failures, 10 successful (20160831/tbxfload-246)
DMAR: DRHD: handling fault status reg 2
DMAR: [INTR-REMAP] Request device [f0:1f.0] fault index 2010 [fault reason 34] Present field in the IRTE entry is clear
</code></pre></div></div>

<h4 id="mce-hardware-error">mce hardware error</h4>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mce_notify_irq: 1 callbacks supressed
mce: [Hardware Error]: machine check events logged
</code></pre></div></div>

<p>Some reports of these here:
<a href="http://en.community.dell.com/techcenter/os-applications/f/4613/t/19997490">http://en.community.dell.com/techcenter/os-applications/f/4613/t/19997490</a></p>

<p>Using <code class="language-plaintext highlighter-rouge">mcelog</code> (<code class="language-plaintext highlighter-rouge">nix-shell -p mcelog</code>) it shows some output, but
requires an update. Our <code class="language-plaintext highlighter-rouge">mcelog</code> is out of date. I updated it in
unstable, and created an <code class="language-plaintext highlighter-rouge">mcelog</code> service. Nothing too scary in the
log, not sure what it is.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>See: [https://github.com/andikleen/mcelog/blob/master/mcelog.service](https://github.com/andikleen/mcelog/blob/master/mcelog.service)
</code></pre></div></div>

<h4 id="int3403-thermal-probe-of-int340306-failed-with-error--22"><code class="language-plaintext highlighter-rouge">int3403 thermal: probe of INT3403:06 failed with error -22</code></h4>

<h4 id="other-notes">Other Notes</h4>
<ul>
  <li>In the console, closing the lid causes it to go to sleep in some
fashion, but opening the lid again doesn’t get the screen back.</li>
  <li>In X with no extra configuration, closing the lid and opening it
sleeps and then restores the display correctly.</li>
</ul>

<h3 id="non-problems">Non-Problems</h3>
<h4 id="what-are-dell-dmi-hotkeys">What are “Dell DMI hotkeys”?</h4>
<h4 id="output-after-entering-the-crypt-password">output after entering the crypt password</h4>
]]></description>
        <link>http://grahamc.com/blog/nixos-on-dell-9560/</link>
    </item>
	
    
    
    <item>
        <guid>http://grahamc.com//blog/timemachine-backups-linux-nixos/</guid>
        <title>How to use a NixOS Linux Server for Time Machine Backups</title>
        <pubDate>Wed, 10 May 2017 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Several years ago I remember researching this process for Ubuntu, and
<a href="https://kremalicious.com/ubuntu-as-mac-file-server-and-time-machine-volume/">kremalicious.com</a>
had the best instructions out there. When looking around this time, I
was dismayed to find the process hasn’t seemingly improved for most
Linuxes: custom compilation, fakeroots, and the like.</p>

<p>However, I believe NixOS provides the most succinct and reproducible
instructions yet, without even having to muck with <code class="language-plaintext highlighter-rouge">defaults write</code>.</p>

<p>Incorporate the following in to your <code class="language-plaintext highlighter-rouge">configuration.nix</code>,
<code class="language-plaintext highlighter-rouge">nixos-rebuild switch</code>, and you’re done:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="p">{</span>
      <span class="nv">networking</span><span class="o">.</span><span class="nv">firewall</span><span class="o">.</span><span class="nv">allowedTCPPorts</span> <span class="o">=</span> <span class="p">[</span>
        <span class="mi">548</span> <span class="c"># netatalk</span>
      <span class="p">];</span>

      <span class="nv">services</span> <span class="o">=</span> <span class="p">{</span>
        <span class="nv">netatalk</span> <span class="o">=</span> <span class="p">{</span>
          <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

          <span class="nv">volumes</span> <span class="o">=</span> <span class="p">{</span>
            <span class="s2">"grahamc-time-machine"</span> <span class="o">=</span> <span class="p">{</span>
              <span class="s2">"time machine"</span> <span class="o">=</span> <span class="s2">"yes"</span><span class="p">;</span>
              <span class="nv">path</span> <span class="o">=</span> <span class="s2">"/home/grahamc/time-machine"</span><span class="p">;</span>
              <span class="s2">"valid users"</span> <span class="o">=</span> <span class="s2">"grahamc"</span><span class="p">;</span>
            <span class="p">};</span>
          <span class="p">};</span>
        <span class="p">};</span>

        <span class="nv">avahi</span> <span class="o">=</span> <span class="p">{</span>
          <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
          <span class="nv">nssmdns</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

          <span class="nv">publish</span> <span class="o">=</span> <span class="p">{</span>
            <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
            <span class="nv">userServices</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
          <span class="p">};</span>
        <span class="p">};</span>
      <span class="p">};</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>Tested on NixOS 17.03.</p>
]]></description>
        <link>http://grahamc.com/blog/timemachine-backups-linux-nixos/</link>
    </item>
	
    
</channel>
</rss>
