<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Michael Stapelbergs Website: posts tagged nix</title>
  <link href="https://michael.stapelberg.ch/posts/tags/nix/feed.xml" rel="self"/>
  <link href="https://michael.stapelberg.ch/posts/tags/nix/"/>


  <id>https://michael.stapelberg.ch/posts/tags/nix/</id>
  <generator>Hugo -- gohugo.io</generator>
  <entry>
    <title type="html"><![CDATA[Coding Agent VMs on NixOS with microvm.nix]]></title>
    <link href="https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/"/>
    <id>https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/</id>
    <published>2026-02-01T09:00:00+01:00</published>
    <content type="html"><![CDATA[<p>I have come to appreciate <a href="https://en.wikipedia.org/wiki/AI-assisted_software_development">coding
agents</a> to be
valuable tools for working with computer program code in any capacity, such as
learning about any program’s architecture, diagnosing bugs or developing proofs
of concept. Depending on the use-case, reviewing each command the agent wants to
run can get tedious and time-consuming very quickly. To safely run a coding
agent without review, I wanted a Virtual Machine (VM) solution where the agent
has no access to my personal files and where it’s no big deal if the agent gets
compromised by malware: I can just throw away the VM and start over.</p>
<p>Instead of setting up a stateful VM and re-installing it when needed (ugh!), I
prefer the model of ephemeral VMs where nothing persists on disk, except for
what is explicitly shared with the host.</p>
<p>The <a href="https://github.com/microvm-nix/microvm.nix"><code>microvm.nix</code> project</a> makes it
easy to create such VMs on NixOS, and this article shows you how I like to set
up my VMs.</p>
<h2 id="see-also">See also</h2>
<p>If you haven’t heard of NixOS before, check out <a href="https://en.wikipedia.org/wiki/NixOS">the NixOS Wikipedia
page</a> and
<a href="https://nixos.org/">nixos.org</a>. I <a href="/talks/#2025">spoke about why I switched to Nix in
2025</a> and have published a <a href="/posts/tags/nix/">few blog posts about
Nix</a>.</p>
<p>For understanding the threat model of AI agents, read <a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/">Simon Willison’s “The
lethal trifecta for AI agents: private data, untrusted content, and external
communication” (June
2025)</a>. This
article’s approach to working with the threat model is to remove the “private
data” part from the equation.</p>
<p>If you want to learn about the whole field of sandboxing, check out <a href="https://www.luiscardoso.dev/blog/sandboxes-for-ai">Luis
Cardoso’s “A field guide to sandboxes for AI” (Jan
2026)</a>. I will not be
comparing different solutions in this article, I will just show you one possible
path.</p>
<p>And lastly, maybe you’re not in the mood to build/run sandboxing infrastructure
yourself. Good news: Sandboxing is a hot topic and there are many commercial
offerings popping up that address this need. For example, David Crawshaw and
Josh Bleecher Snyder (I know both from the Go community) recently launched
<a href="https://blog.exe.dev/meet-exe.dev">exe.dev</a>, an agent-friendly VM hosting
service. Another example is <a href="https://fly.io/blog/code-and-let-live/">Fly.io, who launched
Sprites</a>.</p>
<h2 id="setting-up-microvmnix">Setting up microvm.nix</h2>
<p>Let’s jump right in! The next sections walk you through how I set up my config.</p>
<h3 id="step-1-network-prep">Step 1: network prep</h3>
<p>First, I created a new <code>microbr</code> bridge which uses <code>192.168.33.1/24</code> as IP address range and NATs out of the <code>eno1</code> network interface. All <code>microvm*</code> interfaces will be added to that bridge:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>netdevs<span style="color:#666">.</span><span style="color:#4070a0">&#34;20-microbr&#34;</span><span style="color:#666">.</span>netdevConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  Kind <span style="color:#666">=</span> <span style="color:#4070a0">&#34;bridge&#34;</span>;
</span></span><span style="display:flex;"><span>  Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;microbr&#34;</span>;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>networks<span style="color:#666">.</span><span style="color:#4070a0">&#34;20-microbr&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  matchConfig<span style="color:#666">.</span>Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;microbr&#34;</span>;
</span></span><span style="display:flex;"><span>  addresses <span style="color:#666">=</span> [ { Address <span style="color:#666">=</span> <span style="color:#4070a0">&#34;192.168.83.1/24&#34;</span>; } ];
</span></span><span style="display:flex;"><span>  networkConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    ConfigureWithoutCarrier <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>networks<span style="color:#666">.</span><span style="color:#4070a0">&#34;21-microvm-tap&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  matchConfig<span style="color:#666">.</span>Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;microvm*&#34;</span>;
</span></span><span style="display:flex;"><span>  networkConfig<span style="color:#666">.</span>Bridge <span style="color:#666">=</span> <span style="color:#4070a0">&#34;microbr&#34;</span>;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>networking<span style="color:#666">.</span>nat <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  internalInterfaces <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;microbr&#34;</span> ];
</span></span><span style="display:flex;"><span>  externalInterface <span style="color:#666">=</span> <span style="color:#4070a0">&#34;eno1&#34;</span>;
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><h3 id="step-2-flakenix">Step 2: <code>flake.nix</code></h3>
<p>Then, I added the <code>microvm</code> module as a new input to my <code>flake.nix</code> (check out
the <a href="https://microvm-nix.github.io/microvm.nix/">microvm.nix documentation</a> for
details) and enabled the <code>microvm.nixosModules.host</code> module on the NixOS
configuration for my PC (midna). I also created a new <code>microvm.nix</code> file, in
which I declare all my VMs. Here’s what my <code>flake.nix</code> looks like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    nixpkgs <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nixos/nixpkgs/nixos-25.11&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#60a0b0;font-style:italic"># For more recent claude-code</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    nixpkgs-unstable <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nixos/nixpkgs/nixos-unstable&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    };
</span></span><span style="display:flex;"><span>    stapelbergnix <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:stapelberg/nix&#34;</span>;
</span></span><span style="display:flex;"><span>      inputs<span style="color:#666">.</span>nixpkgs<span style="color:#666">.</span>follows <span style="color:#666">=</span> <span style="color:#4070a0">&#34;nixpkgs&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    zkjnastools <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:stapelberg/zkj-nas-tools&#34;</span>;
</span></span><span style="display:flex;"><span>      inputs<span style="color:#666">.</span>nixpkgs<span style="color:#666">.</span>follows <span style="color:#666">=</span> <span style="color:#4070a0">&#34;nixpkgs&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    microvm <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:microvm-nix/microvm.nix&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      inputs<span style="color:#666">.</span>nixpkgs<span style="color:#666">.</span>follows <span style="color:#666">=</span> <span style="color:#4070a0">&#34;nixpkgs&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    };
</span></span><span style="display:flex;"><span>    home-manager <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nix-community/home-manager/release-25.11&#34;</span>;
</span></span><span style="display:flex;"><span>      inputs<span style="color:#666">.</span>nixpkgs<span style="color:#666">.</span>follows <span style="color:#666">=</span> <span style="color:#4070a0">&#34;nixpkgs&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    configfiles <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:stapelberg/configfiles&#34;</span>;
</span></span><span style="display:flex;"><span>      flake <span style="color:#666">=</span> <span style="color:#60add5">false</span>; <span style="color:#60a0b0;font-style:italic"># repo is not a flake</span>
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      self<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      stapelbergnix<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      zkjnastools<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      nixpkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      nixpkgs-unstable<span style="color:#666">,</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      microvm<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      home-manager<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      configfiles<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>    }<span style="color:#666">@</span>inputs:
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>      system <span style="color:#666">=</span> <span style="color:#4070a0">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      pkgs <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> nixpkgs {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>        config<span style="color:#666">.</span>allowUnfree <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      pkgs-unstable <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> nixpkgs-unstable {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>        config<span style="color:#666">.</span>allowUnfree <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      nixosConfigurations <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>        midna <span style="color:#666">=</span> nixpkgs<span style="color:#666">.</span>lib<span style="color:#666">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>          system <span style="color:#666">=</span> <span style="color:#4070a0">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>          specialArgs <span style="color:#666">=</span> { <span style="color:#007020;font-weight:bold">inherit</span> inputs; };
</span></span><span style="display:flex;"><span>          modules <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>            (<span style="color:#007020;font-weight:bold">import</span> <span style="color:#235388">./configuration.nix</span>)
</span></span><span style="display:flex;"><span>            stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>userSettings
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># Use systemd for network configuration</span>
</span></span><span style="display:flex;"><span>            stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>systemdNetwork
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># Use systemd-boot as bootloader</span>
</span></span><span style="display:flex;"><span>            stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>systemdBoot
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># Run prometheus node exporter in tailnet</span>
</span></span><span style="display:flex;"><span>            stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>prometheusNode
</span></span><span style="display:flex;"><span>            zkjnastools<span style="color:#666">.</span>nixosModules<span style="color:#666">.</span>zkjbackup
</span></span><span style="display:flex; background-color:#d8d8d8"><span>            microvm<span style="color:#666">.</span>nixosModules<span style="color:#666">.</span>host
</span></span><span style="display:flex; background-color:#d8d8d8"><span>            <span style="color:#235388">./microvm.nix</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<h3 id="step-3-microvmnix">Step 3: <code>microvm.nix</code></h3>
<p>The following <code>microvm.nix</code> declares two microvms, one for Emacs (about which I wanted to learn more) and one for Go Protobuf, a code base I am familiar with and can use to understand Claude’s capabilities:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  config<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  lib<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  pkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  inputs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>}:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">inherit</span> (inputs)
</span></span><span style="display:flex;"><span>    nixpkgs-unstable
</span></span><span style="display:flex;"><span>    stapelbergnix
</span></span><span style="display:flex;"><span>    microvm
</span></span><span style="display:flex;"><span>    configfiles
</span></span><span style="display:flex;"><span>    home-manager
</span></span><span style="display:flex;"><span>    ;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  microvmBase <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> <span style="color:#235388">./microvm-base.nix</span>;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  microvm<span style="color:#666">.</span>vms<span style="color:#666">.</span>emacsvm <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    autostart <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>    config <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      imports <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>userSettings
</span></span><span style="display:flex;"><span>        microvm<span style="color:#666">.</span>nixosModules<span style="color:#666">.</span>microvm
</span></span><span style="display:flex;"><span>        (microvmBase {
</span></span><span style="display:flex;"><span>          hostName <span style="color:#666">=</span> <span style="color:#4070a0">&#34;emacsvm&#34;</span>;
</span></span><span style="display:flex;"><span>          ipAddress <span style="color:#666">=</span> <span style="color:#4070a0">&#34;192.168.83.6&#34;</span>;
</span></span><span style="display:flex;"><span>          tapId <span style="color:#666">=</span> <span style="color:#4070a0">&#34;microvm4&#34;</span>;
</span></span><span style="display:flex;"><span>          mac <span style="color:#666">=</span> <span style="color:#4070a0">&#34;02:00:00:00:00:05&#34;</span>;
</span></span><span style="display:flex;"><span>          workspace <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/home/michael/microvm/emacs&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#007020;font-weight:bold">inherit</span>
</span></span><span style="display:flex;"><span>            nixpkgs-unstable
</span></span><span style="display:flex;"><span>            configfiles
</span></span><span style="display:flex;"><span>            home-manager
</span></span><span style="display:flex;"><span>            stapelbergnix
</span></span><span style="display:flex;"><span>            ;
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>        <span style="color:#235388">./microvms/emacs.nix</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  microvm<span style="color:#666">.</span>vms<span style="color:#666">.</span>goprotobufvm <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    autostart <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>    config <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      imports <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>userSettings
</span></span><span style="display:flex;"><span>        microvm<span style="color:#666">.</span>nixosModules<span style="color:#666">.</span>microvm
</span></span><span style="display:flex;"><span>        (microvmBase {
</span></span><span style="display:flex;"><span>          hostName <span style="color:#666">=</span> <span style="color:#4070a0">&#34;goprotobufvm&#34;</span>;
</span></span><span style="display:flex;"><span>          ipAddress <span style="color:#666">=</span> <span style="color:#4070a0">&#34;192.168.83.7&#34;</span>;
</span></span><span style="display:flex;"><span>          tapId <span style="color:#666">=</span> <span style="color:#4070a0">&#34;microvm5&#34;</span>;
</span></span><span style="display:flex;"><span>          mac <span style="color:#666">=</span> <span style="color:#4070a0">&#34;02:00:00:00:00:06&#34;</span>;
</span></span><span style="display:flex;"><span>          workspace <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/home/michael/microvm/goprotobuf&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#007020;font-weight:bold">inherit</span>
</span></span><span style="display:flex;"><span>            nixpkgs-unstable
</span></span><span style="display:flex;"><span>            configfiles
</span></span><span style="display:flex;"><span>            home-manager
</span></span><span style="display:flex;"><span>            stapelbergnix
</span></span><span style="display:flex;"><span>            ;
</span></span><span style="display:flex;"><span>          extraZshInit <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            export GOPATH=$HOME/go
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            export PATH=$GOPATH/bin:$PATH
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>        <span style="color:#235388">./microvms/goprotobuf.nix</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="step-4-microvm-basenix">Step 4: <code>microvm-base.nix</code></h3>
<p>The <code>microvm-base.nix</code> module takes these parameters and declares:</p>
<ul>
<li>Network settings: I like using <a href="https://manpages.debian.org/systemd-networkd.8"><code>systemd-networkd(8)</code></a>
 and <a href="https://manpages.debian.org/systemd-resolved.8"><code>systemd-resolved(8)</code></a>
.</li>
<li>Shared directories for:
<ul>
<li>the workspace directory, e.g. <code>~/microvm/emacs</code></li>
<li>the host’s Nix store, so the VM can access software from cache (often)</li>
<li>this VM’s SSH host keys</li>
<li><code>~/claude-microvm</code>, which is a separate state directory, used only on the microvms.</li>
</ul>
</li>
<li>an 8 GB disk overlay (var.img), stored in <code>/var/lib/microvms/&lt;name&gt;</code></li>
<li><code>cloud-hypervisor</code> (QEMU also works well!) as the hypervisor, with 8 vCPUs and 4 GB RAM.</li>
<li>A workaround for systemd trying to unmount <code>/nix/store</code> (which causes a deadlock).</li>
</ul>
<details>
<summary>Expand full <code>microvm-base.nix</code> code</summary>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  hostName<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  ipAddress<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  tapId<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  mac<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  workspace<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  nixpkgs-unstable<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  configfiles<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  home-manager<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  stapelbergnix<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  extraZshInit <span style="color:#666">?</span> <span style="color:#4070a0">&#34;&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>}:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  config<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  lib<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  pkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>}:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>  system <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>stdenv<span style="color:#666">.</span>hostPlatform<span style="color:#666">.</span>system;
</span></span><span style="display:flex;"><span>  pkgsUnstable <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> nixpkgs-unstable {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>    config<span style="color:#666">.</span>allowUnfree <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  imports <span style="color:#666">=</span> [ home-manager<span style="color:#666">.</span>nixosModules<span style="color:#666">.</span>home-manager ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># home-manager configuration</span>
</span></span><span style="display:flex;"><span>  home-manager<span style="color:#666">.</span>useGlobalPkgs <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  home-manager<span style="color:#666">.</span>useUserPackages <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  home-manager<span style="color:#666">.</span>extraSpecialArgs <span style="color:#666">=</span> { <span style="color:#007020;font-weight:bold">inherit</span> configfiles stapelbergnix; };
</span></span><span style="display:flex;"><span>  home-manager<span style="color:#666">.</span>users<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    imports <span style="color:#666">=</span> [ <span style="color:#235388">./microvm-home.nix</span> ];
</span></span><span style="display:flex;"><span>    microvm<span style="color:#666">.</span>extraZshInit <span style="color:#666">=</span> extraZshInit;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Claude Code CLI (from nixpkgs-unstable, unfree)</span>
</span></span><span style="display:flex;"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    pkgsUnstable<span style="color:#666">.</span>claude-code
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>hostName <span style="color:#666">=</span> hostName;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  system<span style="color:#666">.</span>stateVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#34;25.11&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>openssh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># To match midna (host)</span>
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>groups<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    gid <span style="color:#666">=</span> <span style="color:#40a070">1000</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    group <span style="color:#666">=</span> <span style="color:#4070a0">&#34;michael&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>resolved<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>useDHCP <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>useNetworkd <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>tempAddresses <span style="color:#666">=</span> <span style="color:#4070a0">&#34;disabled&#34;</span>;
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>networks<span style="color:#666">.</span><span style="color:#4070a0">&#34;10-e&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    matchConfig<span style="color:#666">.</span>Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;e*&#34;</span>;
</span></span><span style="display:flex;"><span>    addresses <span style="color:#666">=</span> [ { Address <span style="color:#666">=</span> <span style="color:#4070a0">&#34;</span><span style="color:#70a0d0">${</span>ipAddress<span style="color:#70a0d0">}</span><span style="color:#4070a0">/24&#34;</span>; } ];
</span></span><span style="display:flex;"><span>    routes <span style="color:#666">=</span> [ { Gateway <span style="color:#666">=</span> <span style="color:#4070a0">&#34;192.168.83.1&#34;</span>; } ];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>nameservers <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;8.8.8.8&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;1.1.1.1&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Disable firewall for faster boot and less hassle;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># we are behind a layer of NAT anyway.</span>
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>firewall<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>settings<span style="color:#666">.</span>Manager <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># fast shutdowns/reboots! https://mas.to/@zekjur/113109742103219075</span>
</span></span><span style="display:flex;"><span>    DefaultTimeoutStopSec <span style="color:#666">=</span> <span style="color:#4070a0">&#34;5s&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Fix for microvm shutdown hang (issue #170):</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Without this, systemd tries to unmount /nix/store during shutdown,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># but umount lives in /nix/store, causing a deadlock.</span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>mounts <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      what <span style="color:#666">=</span> <span style="color:#4070a0">&#34;store&#34;</span>;
</span></span><span style="display:flex;"><span>      where <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/nix/store&#34;</span>;
</span></span><span style="display:flex;"><span>      overrideStrategy <span style="color:#666">=</span> <span style="color:#4070a0">&#34;asDropin&#34;</span>;
</span></span><span style="display:flex;"><span>      unitConfig<span style="color:#666">.</span>DefaultDependencies <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Use SSH host keys mounted from outside the VM (remain identical).</span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>openssh<span style="color:#666">.</span>hostKeys <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      path <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/etc/ssh/host-keys/ssh_host_ed25519_key&#34;</span>;
</span></span><span style="display:flex;"><span>      type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;ed25519&#34;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  microvm <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># Enable writable nix store overlay so nix-daemon works.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># This is required for home-manager activation.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># Uses tmpfs by default (ephemeral), which is fine since we</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># don&#39;t build anything in the VM.</span>
</span></span><span style="display:flex;"><span>    writableStoreOverlay <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/nix/.rw-store&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    volumes <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        mountPoint <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/var&#34;</span>;
</span></span><span style="display:flex;"><span>        image <span style="color:#666">=</span> <span style="color:#4070a0">&#34;var.img&#34;</span>;
</span></span><span style="display:flex;"><span>        size <span style="color:#666">=</span> <span style="color:#40a070">8192</span>; <span style="color:#60a0b0;font-style:italic"># MB</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    shares <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># use proto = &#34;virtiofs&#34; for MicroVMs that are started by systemd</span>
</span></span><span style="display:flex;"><span>        proto <span style="color:#666">=</span> <span style="color:#4070a0">&#34;virtiofs&#34;</span>;
</span></span><span style="display:flex;"><span>        tag <span style="color:#666">=</span> <span style="color:#4070a0">&#34;ro-store&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># a host&#39;s /nix/store will be picked up so that no</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># squashfs/erofs will be built for it.</span>
</span></span><span style="display:flex;"><span>        source <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/nix/store&#34;</span>;
</span></span><span style="display:flex;"><span>        mountPoint <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/nix/.ro-store&#34;</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        proto <span style="color:#666">=</span> <span style="color:#4070a0">&#34;virtiofs&#34;</span>;
</span></span><span style="display:flex;"><span>        tag <span style="color:#666">=</span> <span style="color:#4070a0">&#34;ssh-keys&#34;</span>;
</span></span><span style="display:flex;"><span>        source <span style="color:#666">=</span> <span style="color:#4070a0">&#34;</span><span style="color:#70a0d0">${</span>workspace<span style="color:#70a0d0">}</span><span style="color:#4070a0">/ssh-host-keys&#34;</span>;
</span></span><span style="display:flex;"><span>        mountPoint <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/etc/ssh/host-keys&#34;</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        proto <span style="color:#666">=</span> <span style="color:#4070a0">&#34;virtiofs&#34;</span>;
</span></span><span style="display:flex;"><span>        tag <span style="color:#666">=</span> <span style="color:#4070a0">&#34;claude-credentials&#34;</span>;
</span></span><span style="display:flex;"><span>        source <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/home/michael/claude-microvm&#34;</span>;
</span></span><span style="display:flex;"><span>        mountPoint <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/home/michael/claude-microvm&#34;</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        proto <span style="color:#666">=</span> <span style="color:#4070a0">&#34;virtiofs&#34;</span>;
</span></span><span style="display:flex;"><span>        tag <span style="color:#666">=</span> <span style="color:#4070a0">&#34;workspace&#34;</span>;
</span></span><span style="display:flex;"><span>        source <span style="color:#666">=</span> workspace;
</span></span><span style="display:flex;"><span>        mountPoint <span style="color:#666">=</span> workspace;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    interfaces <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;tap&#34;</span>;
</span></span><span style="display:flex;"><span>        id <span style="color:#666">=</span> tapId;
</span></span><span style="display:flex;"><span>        mac <span style="color:#666">=</span> mac;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    hypervisor <span style="color:#666">=</span> <span style="color:#4070a0">&#34;cloud-hypervisor&#34;</span>;
</span></span><span style="display:flex;"><span>    vcpu <span style="color:#666">=</span> <span style="color:#40a070">8</span>;
</span></span><span style="display:flex;"><span>    mem <span style="color:#666">=</span> <span style="color:#40a070">4096</span>;
</span></span><span style="display:flex;"><span>    socket <span style="color:#666">=</span> <span style="color:#4070a0">&#34;control.socket&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></details>
<h3 id="step-5-microvm-homenix">Step 5: <code>microvm-home.nix</code></h3>
<p><code>microvm-base.nix</code> in turn pulls in <code>microvm-home.nix</code>, which sets up home-manager to:</p>
<ul>
<li>Set up Zsh with my configuration</li>
<li>Set up Emacs with my configuration</li>
<li>Set up Claude Code in shared directory <code>~/claude-microvm</code>.</li>
</ul>
<details>
<summary>Expand full <code>microvm-home.nix</code> code</summary>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  config<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  pkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  lib<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  configfiles<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  stapelbergnix<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>}:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  options<span style="color:#666">.</span>microvm <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    extraZshInit <span style="color:#666">=</span> lib<span style="color:#666">.</span>mkOption {
</span></span><span style="display:flex;"><span>      type <span style="color:#666">=</span> lib<span style="color:#666">.</span>types<span style="color:#666">.</span>lines;
</span></span><span style="display:flex;"><span>      default <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>      description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Extra lines to add to zsh initContent&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  config <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    home<span style="color:#666">.</span>username <span style="color:#666">=</span> <span style="color:#4070a0">&#34;michael&#34;</span>;
</span></span><span style="display:flex;"><span>    home<span style="color:#666">.</span>homeDirectory <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/home/michael&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    programs<span style="color:#666">.</span>zsh <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>      history <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>        size <span style="color:#666">=</span> <span style="color:#40a070">4000</span>;
</span></span><span style="display:flex;"><span>        save <span style="color:#666">=</span> <span style="color:#40a070">10000000</span>;
</span></span><span style="display:flex;"><span>        ignoreDups <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>        share <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>        append <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      initContent <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        </span><span style="color:#70a0d0">${</span><span style="color:#007020">builtins</span><span style="color:#666">.</span>readFile <span style="color:#4070a0">&#34;</span><span style="color:#70a0d0">${</span>configfiles<span style="color:#70a0d0">}</span><span style="color:#4070a0">/zshrc&#34;</span><span style="color:#70a0d0">}</span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        export CLAUDE_CONFIG_DIR=/home/michael/claude-microvm
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        </span><span style="color:#70a0d0">${</span>config<span style="color:#666">.</span>microvm<span style="color:#666">.</span>extraZshInit<span style="color:#70a0d0">}</span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    programs<span style="color:#666">.</span>emacs <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>      package <span style="color:#666">=</span> stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>emacsWithPackages { <span style="color:#007020;font-weight:bold">inherit</span> pkgs; };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    home<span style="color:#666">.</span>file<span style="color:#666">.</span><span style="color:#4070a0">&#34;.config/emacs&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      source <span style="color:#666">=</span> <span style="color:#4070a0">&#34;</span><span style="color:#70a0d0">${</span>configfiles<span style="color:#70a0d0">}</span><span style="color:#4070a0">/config/emacs&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    home<span style="color:#666">.</span>stateVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#34;25.11&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    programs<span style="color:#666">.</span>home-manager<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></details>
<h3 id="step-6-goprotobufnix">Step 6: <code>goprotobuf.nix</code></h3>
<p>The <code>goprotobuf.nix</code> makes available a bunch of required and convenient packages:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># Project-specific configuration for goprotobufvm</span>
</span></span><span style="display:flex;"><span>{ pkgs<span style="color:#666">,</span> <span style="color:#666">...</span> }:
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Development environment for Go Protobuf</span>
</span></span><span style="display:flex;"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># Go toolchain</span>
</span></span><span style="display:flex;"><span>    go
</span></span><span style="display:flex;"><span>    gopls
</span></span><span style="display:flex;"><span>    delve
</span></span><span style="display:flex;"><span>    protobuf
</span></span><span style="display:flex;"><span>    gnumake
</span></span><span style="display:flex;"><span>    gcc
</span></span><span style="display:flex;"><span>    git
</span></span><span style="display:flex;"><span>    ripgrep
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="running-the-vm">Running the VM</h3>
<p>Let’s create the workspace directory and create an SSH host key:</p>
<pre tabindex="0"><code>mkdir -p ~/microvm/emacs/ssh-host-keys
ssh-keygen -t ed25519 -N &#34;&#34; \
  -f ~/microvm/emacs/ssh-host-keys/ssh_host_ed25519_key
</code></pre><p>Now we can start the VM:</p>
<pre tabindex="0"><code>sudo systemctl start microvm@emacsvm
</code></pre><p>It boots and responds to pings within a few seconds.</p>
<p>Then, SSH into the VM (perhaps in a <a href="https://manpages.debian.org/tmux.1"><code>tmux(1)</code></a>
 session) and run Claude
(or your Coding Agent of choice) without permission prompts in the shared
workspace directory:</p>
<pre tabindex="0"><code>% ssh 192.168.83.2
emacsvm% cd microvm/emacs
emacsvm% claude --dangerously-skip-permissions
</code></pre><p>This is what running Claude in such a setup looks like:</p>















<a href="https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/2026-01-28-neofetch-featured.png"><img
  srcset="https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/2026-01-28-neofetch-featured_hu_51eebb097010a828.png 2x,https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/2026-01-28-neofetch-featured_hu_9f1cbedec722fa16.png 3x"
  src="https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/2026-01-28-neofetch-featured_hu_d06e7aa7176833b3.png"
  alt="Claude Code in “bypass permissions” mode" title="Claude Code in “bypass permissions” mode"
  width="600"
  height="479"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<h2 id="creating-vms-with-claude">Creating VMs with Claude</h2>
<p>After going through the process of setting up a MicroVM once, it becomes tedious.</p>
<p>I was curious if <a href="https://code.claude.com/docs/en/skills">Claude Skills</a> could
help with a task like this. Skills are markdown files that instruct Claude to do
certain steps in certain situations.</p>
<p>I created <code>.claude/skills/create-microvm/SKILL.md</code> as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>name: create-microvm
</span></span><span style="display:flex;"><span>description: Creates a new microvm Virtual Machine on midna for running Claude in, with source code repositories and build dependencies available inside the microvm. Use when the user asks to create a new microvm.
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Inspect the existing structure at ~/machines/midna (NixOS configuration using Flakes), which includes several MicroVMs in the ~/machines/midna/microvms/ directory.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Then, create a similar structure for the microvm the user asked to create. Be sure to consider:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">1.</span> Create a new subdirectory for this microvm, named NAME (the microvm name).
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">2.</span> Create an entry in microvm.nix similar to an existing microvm&#39;s, but:
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">3.</span> Change hostname to NAME
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">4.</span> Change IP address (e.g., 192.168.83.3): find used ones and chose next free
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">5.</span> Change workspace share to /home/michael/microvm/NAME
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">6.</span> Include build dependencies for the new microvm based on user request
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">7.</span> Create ssh-host-keys directory
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">8.</span> Generate SSH host keys
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">9.</span> Clone/download source code repositories into this microvm based on user request
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Once the microvm is defined, update ~/machines/midna to pull it in. To verify, build the config (but do not deploy).
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>You need to git-commit changes to the microvm before ~/machines/midna can see them.
</span></span></code></pre></div><p>When using this skill with Claude Code (tested version: v2.0.76 and v2.1.15), with the <a href="https://www.anthropic.com/news/claude-opus-4-5">Opus 4.5 model</a>, I can send a prompt like this:</p>
<blockquote>
<p>please set up a microvm for Debian Code Search (dcs). see ~/dcs for the source code (but clone from <a href="https://github.com/Debian/dcs">https://github.com/Debian/dcs</a>)</p>
</blockquote>
<p>Now Claude churns for a few minutes, possibly asking a clarification question
before that. Afterwards, Claude reports back with:</p>
<blockquote>
<p>The dcsvm microvm has been set up successfully. Here&rsquo;s what was created:</p>
<p>Configuration:</p>
<ul>
<li>/home/michael/machines/midna/microvms/dcs.nix - Project-specific packages (Go toolchain, protobuf tools, uglify-js, yuicompressor,
zopfli)</li>
<li>Updated /home/michael/machines/midna/microvm.nix to include dcsvm with:
<ul>
<li>IP: 192.168.83.8</li>
<li>tapId: microvm6</li>
<li>mac: 02:00:00:00:00:07</li>
</ul>
</li>
</ul>
<p>Workspace:</p>
<ul>
<li>/home/michael/microvm/dcs/ - Workspace directory</li>
<li>/home/michael/microvm/dcs/ssh-host-keys/ - SSH host keys (ed25519)</li>
<li>/home/michael/microvm/dcs/dcs/ - Cloned repository from <a href="https://github.com/Debian/dcs">https://github.com/Debian/dcs</a></li>
</ul>
<p>Build verified - The configuration builds successfully.</p>
<p>To start the microvm after deploying:
<code>systemctl start microvm@dcsvm</code></p>
<p>To SSH into it:
<code>ssh michael@192.168.83.8</code></p>
</blockquote>
<p>Wonderful! In my experience, Claude always got the VM creation correct.</p>
<h2 id="replicating-this-setup-with-claude-code">Replicating this setup with Claude Code</h2>
<p>In fact, you can go one step further: Instead of just asking Claude to create
new MicroVMs, you can also ask Claude to replicate this entire setup into your
NixOS configuration!</p>
<p>Try a prompt like this:</p>
<blockquote>
<p>read
<a href="https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/">https://michael.stapelberg.ch/posts/2026-02-01-coding-agent-microvm-nix/</a>
— I want the exact same setup in my midna NixOS configuration please!</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>NixOS has a reputation of being hard to adopt, but once you are using NixOS, you
can do powerful things like spinning up ephemeral MicroVMs for a new project
within minutes.</p>
<p>The maintenance effort is minimal: When I update my personal PC, my MicroVM
configurations start using the new software versions, too. Customization is easy
if needed.</p>
<p>This actually mirrors my experience with Coding Agents: I don’t feel like
they’re <em>automatically</em> making existing tasks more efficient, I feel that they
make things possible that were previously out of reach (similar to <a href="https://en.wikipedia.org/wiki/Jevons_paradox">Jevons
paradox</a>).</p>
<p>It was fascinating (and scary!) to experience the quality increase of Coding
Agents during 2025. At the beginning of 2025 I thought that LLMs are an
overhyped toy, and felt it was almost insulting when people showed me text or
code produced by these models. But almost every new frontier model release got
significantly better, and by now I have been positively surprised by Claude
Code’s capabilities and quality many times. It has produced code that handles
legitimate edge cases I would not have considered.</p>
<p>With this article, I showed one possible way to run Coding Agents safely (or any
workload that shouldn’t access your private data, really) that you can adjust in
many ways for your needs.</p>
]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[NixCon 2025 Trip Report 🐝]]></title>
    <link href="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/"/>
    <id>https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/</id>
    <published>2025-09-21T09:34:00+02:00</published>
    <content type="html"><![CDATA[<p>I liked the NixOS meetup earlier this year, and at the end of the meetup they
told everyone about NixCon 2025, which would be happening in Switzerland this
year, at the very same location, the <a href="https://www.ost.ch/">University Of Applied Sciences
OST</a> in Rapperswil, so I decided to go! In this trip
report, I want to give you a rough impression of how I experienced this awesome
conference :)</p>
<p><em>The bee in the title is a NixCon inside joke ;)</em></p>
<h2 id="friday">Friday</h2>
<p>I arrived at about 09:30 on a rainy Friday morning, meaning I hurried from the
train station into OST building 1 to show my ticket QR code and pick up my
conference badge and custom name tag that I pre-ordered. The custom ones have
your name engraved and come with a strong magnet to attach them to your clothes:</p>















<a href="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5775.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5775_hu_2ab24eefb66f32dc.jpg 2x,https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5775_hu_5badb8e34d0d757f.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5775_hu_f608afe3d2f3ee1.jpg"
  alt="regular and custom name tag" title="regular and custom name tag"
  width="600"
  height="450"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<p>After grabbing a bite to eat, I headed to the main lecture hall for the opening
session. <a href="https://www.ost.ch/en/person/farhad-d-mehta-8699">Prof. Dr. Farhad
Mehta</a> from OST, as well as
the entire NixCon orga team, welcomed the 450 registered attendees to the 10th
NixCon! I recognized many familiar faces from the Nix meetup, but many hands
went up when the audience was asked for whom it was the first time at NixCon, or
in Switzerland in general.</p>
<p>I want to thank Prof. Mehta in particular for making possible such meetups and
events! 👏</p>
<p>If you work at a university, school or other organisation that has access to
rooms, consider offering to host a meetup (on a regular basis, or even just
once)! Locations are always hard to find, so offering a space is a great
contribution to Open Source.</p>
<h3 id="what-if-github-actions-were-local-first-and-built-using-nix">“What if GitHub Actions were local-first and built using Nix?”</h3>
<p>The first technical talk of the day was “What if GitHub Actions were local-first
and built using Nix?” by Domen Kožar, the person behind
<a href="https://cachix.org">cachix.org</a>, which is a hosted Nix cache. The talk pitched
<a href="https://cloud.devenv.sh/">cloud.devenv.sh</a>, which is a Nix-based CI solution
(like GitHub Actions) using <a href="https://devenv.sh/">devenv</a>.</p>
<p>By using this solution, you solve the problem that you can’t easily / completely
run GitHub Actions locally (yes, we all know about
<a href="https://github.com/nektos/act">act</a>), and you get to (?) write Nix configs
instead of YAML configs.</p>
<p>The solution seems nice, but I found the talk a little unstructured because the
presenter jumped around between slides so much. One crucial question was left
unanswered: How do you integrate this custom solution with your GitHub projects?
To me, diverging from the default way of configuring GitHub Actions does not
seem worth it for my projects. YMMV.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56408-what-if-github-actions">→ watch the recording (46 minutes) on
media.ccc.de</a></p>
<h3 id="rewriting-the-hydra-queue-runner-in-rust">“Rewriting the Hydra Queue Runner in Rust”</h3>
<p>Next up: “Rewriting the Hydra Queue Runner in Rust” by Simon Hauser from
<a href="https://helsinki-systems.de/">Helsinki Systems</a>, a small German software
company. <a href="https://github.com/NixOS/hydra">Hydra</a> is the component in the
NixOS infrastructure which schedules builds: when nixpkgs changes, this is
the component that runs the build whose result ends up on
<a href="https://cache.nixos.org">cache.nixos.org</a> (the Debian equivalent is
<a href="https://wiki.debian.org/buildd">buildd</a>).</p>
<p>Simon explained that bottlenecks in the current queue runner result in
stranding of infrastructure: the project has machines available that it
cannot use fully. He outlined how they replaced a crufty SSH-based automation
with a well-designed gRPC protocol. I got the impression that a group of
people was involved in developing and reviewing this design, which is a great
sign for a healthy project.</p>
<p>One thing that was unfortunately missing from the talk were metrics. It would
have been great to see a few graphs that illustrate just how much better the
rewritten queue runner is.</p>
<p>Currently, the new queue runner is already used for Nix Community builds, but
not yet in production for NixOS itself. Hopefully soon, though!</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56409-rewriting-the-hydra-que">→ watch the recording (27 minutes) on
media.ccc.de</a></p>
<h3 id="you-cant-spell-devshell-without-hell">“You can&rsquo;t spell &ldquo;devshell&rdquo; without &ldquo;hell&rdquo;”</h3>
<p>This talk was presented by Zach Mitchell from <a href="https://flox.dev/">Flox</a>, which
is a Nix-based dev environment solution. Thus far, I use <code>nix-shell</code> or <code>nix develop</code> (see <a href="/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/">Development shells with Nix: four quick
examples</a>), so I was
curious what I’d learn from this talk.</p>
<p>Zach explained that both, <code>nix-shell</code> and <code>nix develop</code> were originally
written to debug Nix package builds, not to provide general-purpose
development environments. For users, this manifests as not being able to use
your favorite shell — <code>nix develop</code> only supports Bash. One might read about
<code>nix develop -c exec &lt;shell&gt;</code>, but that’s wrong, because then the shell’s RC
files run <strong>after</strong> Nix setup, possibly destroying parts of the setup.</p>
<p>One interesting thing I learnt is that the Nix garbage collector scans
<code>/proc</code> to avoid removing Nix store paths that are still needed by running
processes.</p>
<p>Zach mentioned <a href="https://github.com/zmitchell/proctrace">https://github.com/zmitchell/proctrace</a>, which is a
bpftrace-based profiler that tracks forks/execs and generates gantt chart
syntax of the timing. Sounds cool, but is unfortunately broken right now…?
Too bad.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56410-you-cant-spell-devshell">→ watch the recording (45 minutes) on
media.ccc.de</a></p>
<h3 id="the-nix-binary-cache-and-aws">“The Nix Binary Cache and AWS”</h3>
<p>In this fireside chat, Tarus Balog shared how he ended up at AWS after 20 years
of Open Source, and how his team wants to give back to the community. One
specific way in which they’re doing that is by hosting cache.nixos.org.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56403-the-nix-binary-cache-an">→ watch the recording (24 minutes) on
media.ccc.de</a></p>
<h3 id="nix-based-development-environments-at-shopify-reprise">“Nix-based development environments at Shopify (reprise)”</h3>
<p>Josh Heinrichs from Shopify shared how they adopted Nix (again!), and I think
real-world enterprise adoption stories like these are very interesting.</p>
<p>In summary, Shopify had a <code>dev</code> command (since 2016), which offered declarative
configuration and then dispatched to <code>apt</code> (Linux) or <code>homebrew</code> (macOS). In the
first attempt to move to Nix, the effort didn’t reach stable footing (some folks
couldn’t use it yet) and then a company-wide shift to cloud development
happened, where the easier solution was to “just use ubuntu”.</p>
<p>A few years in, folks are apparently not so happy with the cloud development
environments and one day, Shopify CEO <a href="https://en.wikipedia.org/wiki/Tobias_L%C3%BCtke">Tobias
Lütke</a> finds
<a href="https://devenv.sh">devenv</a>, which is a Nix-based solution that is remarkably
similar to Shopify’s <code>dev</code>. So Tobi adopts devenv for one of their services and
becomes supportive of using Nix. This time around, they spend a lot more time on
a successful rollout within the organization, meaning incremental adoption,
getting all stakeholders on board, etc.</p>
<p>The takeaway is that one specific, well-supported use-case can be the adoption
driver. And once you have your development environments on a Nix-based solution,
you can more easily adopt other parts of the ecosystem as well.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56413-nix-based-development-e">→ watch the recording (19 minutes) on
media.ccc.de</a></p>
<h3 id="my-first-nix-aha-a-newcomers-perspective">“My first Nix Aha!: A Newcomer’s Perspective”</h3>
<p>In a similar spirit to the Shopify talk, Kavisha Kumar from ASML shared how she
got into Nix after seeing a colleague use <code>nix-shell</code> to obtain a clean
development shell.</p>
<p>Kavisha spent a lot of time at ASML to teach others about why and how to use
Nix. She shared a number of nice metaphors that explained Nix concepts through
the subject area of video gaming.</p>
<p>I think many people are excited about Nix, but have trouble conveying that
excitement to others. Kavisha showed us a good way that worked for her.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56414-my-first-nix-aha-a-newc">→ watch the recording (19 minutes) on
media.ccc.de</a></p>
<h3 id="lightning-talks">Lightning Talks</h3>
<p>The rest of the day was filled with lightning talks.</p>
<p>Cole Mickens from <a href="https://determinate.systems/">Determinate Systems</a> explained
what features they are currently shipping in their downstream distribution
“Determinate Nix” (features will be upstreamed): lazy trees (a performance
optimization for evaluating Flakes), parallel evaluation (brings evaluation
times down from 16s to 7s) and a native Linux builder for mac. Next up are Flake
Schemas, which I haven’t read about yet.</p>
<p>Yvan Sraka from <a href="https://numtide.com/">Numtide</a>, a Nix and DevOps consultancy,
showed how he manages Linux machines for friends and family with NixOS. He has
his own configuration layer on top of NixOS and only uses the system as a
base. Most actual programs are used through AppImage, Flatpaks,
<a href="https://github.com/Mic92/envfs">envfs</a> and
<a href="https://github.com/nix-community/nix-ld">nix-ld</a>. The latter two are solutions
to use FHS based programs (those that expect <code>/usr/bin</code> and other standard
locations to be present) on non-FHS systems like NixOS. I had heard of nix-ld
before, but not of envfs.</p>
<p>Jacek Galowicz from <a href="https://nixcademy.com">Nixcademy</a> showed how to use
systemd-sysupdate and systemd-repart to implement A/B style updates with NixOS
and systemd. It’s great to see that this technique is more and more mainstream,
as I am also using A/B style updating successfully in
<a href="https://gokrazy.org/">gokrazy</a>.</p>
<h2 id="saturday">Saturday</h2>
<p>The weather on Saturday was a lot better, so I made sure to get a seat with a
view of Lake Zürich:</p>















<a href="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5754.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5754_hu_707d7fbf62a50ff9.jpg 2x,https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5754_hu_c44ab49e54db14bc.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5754_hu_91ced09efd5ff807.jpg"
  alt="lake view!" title="lake view!"
  width="600"
  height="450"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<h3 id="the-bikes-have-been-shed-the-official-nix-formatter">“The bikes have been shed: The official Nix formatter”</h3>
<p>In this talk, Silvan Mosberger from Tweag (and one of the main NixCon
organizers!), explains how the official formatting tool for .nix files came to
be.</p>
<p>I was delighted to hear <code>gofmt</code>, the official Go formatter, being mentioned as a
source of inspiration. Just like in other language ecosystems, introducing
uniform formatting eliminates time-consuming back-and-forth in code review over
adhering to coding style. Unfortunately, the formatting folks did not replicate
one key aspect to gofmt’s success: gofmt has no options. As the famous Go
proverb goes:</p>
<blockquote>
<p>Gofmt&rsquo;s style is no one&rsquo;s favorite, yet gofmt is everyone&rsquo;s favorite!</p>
</blockquote>
<p>Meaning that it’s more important that everyone uses the same style, compared to
everyone being able to express their personal style preferences.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56375-the-bikes-have-been-she">→ watch the recording (20 minutes) on
media.ccc.de</a></p>
<h3 id="mastering-nixos-integration-tests-advanced-techniques-for-fast-and-robust-multi-vm-tests">“Mastering NixOS Integration Tests: Advanced Techniques for Fast and Robust Multi-VM Tests”</h3>
<p>In this two-hour workshop, Jacek Galowicz from
<a href="https://nixcademy.com">Nixcademy</a>, who is not only a Nix teacher, but also
happens to be the maintainer of the NixOS integration test driver, shows us how
to write complex integration tests with a few lines of Nix and Python.</p>
<p>Jacek showed an integration test example: a Bittorrent service, consisting of
tracker, clients, firewalls and multiple networks! Nixpkgs contains over 1000
such integration tests, and running one on your laptop is easy.</p>
<p>The various ways to debug your tests seem pretty cool: using vsock instead of
port forwardings, and enabling a debug hook that will make a failed test hang
and wait to be debugged.</p>
<p>I thought this was a great overview and Jacek is an engaging teacher. I would
recommend booking his classes!</p>
<h3 id="when-not-to-nix-working-with-external-config-and-sops-nix">“When Not to Nix: Working with External Config and SOPS Nix”</h3>
<p>Ryota spoke about when to use Nix and when not to use Nix. For example, you
could manage your dotfiles (config files) with Nix, or you could decide not
to. Having recently migrated more and more machines and configurations to Nix, I
found myself agreeing with this talk: It’s important to understand what you’ll
get out of declaratively or statefully managed configs, and when which approach
is better.</p>
<p><a href="https://media.ccc.de/v/nixcon2025-56381-when-not-to-nix-working">→ watch the recording (19 minutes) on
media.ccc.de</a></p>
<h3 id="lightning-talks-1">Lightning Talks</h3>
<p>The rest of the day I spent in lightning talks, some of which were sponsored
talk slots. I learnt about, in no particular order:</p>
<ul>
<li><a href="https://www.cloudhypervisor.org/">Cloud Hypervisor</a>, a KVM based hypervisor like qemu, but written in Rust.</li>
<li><a href="https://nixbuild.net/">nixbuild.net</a>, a pay-as-you-go offering for extra
build capacity you can rent. On Sunday I heard someone say that their company
is using nixbuild.net and it’s very smooth.</li>
<li><a href="https://nix-ci.com/">NixCI</a>, a Nix-based hosted CI. So, the cloud.devenv.sh
service we heard about on Friday is a competitor to this service.</li>
<li><a href="https://flox.dev/nixinthewild/">Nix in the Wild</a> is an effort by Flox where
they do 45-60 minute interviews about Nix success stories. This might help you
convince folks in your organization.</li>
<li><a href="https://clan.lol">clan</a> is a fleet management solution.</li>
<li><a href="https://novacustom.com/">NovaCustom</a>, a one-person laptop/PC company. The
laptops come with coreboot and work with NixOS.</li>
<li>ExpressVPN is migrating their internal server setup (TrustedServer) from
Debian to NixOS! Deploying weekly in 105+ countries.</li>
<li>Cyberus, a German company, is offering NixOS LTS releases, compliant with the
EU Cyber Resilience Act obligations.</li>
<li>David’s <a href="https://github.com/dnr/styx">styx</a> project is a more
bandwidth-efficient download mechanism for NixOS updates. This uses
<a href="https://en.wikipedia.org/wiki/EROFS">EROFS</a>, which seems like an interesting
alternative to SquashFS images.</li>
</ul>
<p>After all the talks, we met outside for a group picture followed by barbecue at
the lake:</p>















<a href="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/NixCon2025_arik-grahl.de_CC-BY-SA-4.0-3200w-featured.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/NixCon2025_arik-grahl.de_CC-BY-SA-4.0-3200w-featured_hu_f49e046e26d2953f.jpg 2x,https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/NixCon2025_arik-grahl.de_CC-BY-SA-4.0-3200w-featured_hu_5c0748c0810ca3da.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/NixCon2025_arik-grahl.de_CC-BY-SA-4.0-3200w-featured_hu_8ba18aa31755731f.jpg"
  alt="NixCon 2025 group picture" title="NixCon 2025 group picture"
  width="600"
  height="400"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<p><em>NixCon 2025 by Arik Grahl. Licensed under CC BY-SA 4.0.</em></p>
<h2 id="sunday">Sunday</h2>
<p>Before the conference, I wasn’t sure if I would even bother showing up for
Sunday (Hack day), but on Sunday, I was like “of course!”, and it was a great
decision!</p>
<p>Many people were still around and were working on their projects. It felt like
the answer to any Nix question was just one chat message away — there was
expertise and helping hands from many parts of the project.</p>
<p>I ended up meeting a couple of people I only knew from online interactions
before, and we also talked a lot about meetups. Now, I am invited to multiple
meetups to give a talk :D</p>
<h2 id="conclusion">Conclusion</h2>
<p>This was a wonderful conference! The orga team and all contributors did a great
job!</p>
<p>As always, the OST in Rapperswil is a great venue for Open Source events.</p>
<p>Ticket sales and talk submission / scheduling were done using the
<a href="http://pretix.eu/">Pretix</a> and <a href="https://pretalx.com/p/about/">Pretalx</a> Open
Source systems, which makes me proud to have contributed to Pretix.</p>
<p>The selection of talks was great: Some deeply technical, some covering only the
human side of things, and many somewhere in between. I got the impression that
all the presenters I saw genuinely cared about their topic, so the overall
energy was very good!</p>
<p>(You can watch the talk recordings at <a href="https://media.ccc.de/c/nixcon2025">media.ccc.de: NixCon
2025</a>.)</p>
<p>Also outside of the talks, I had many friendly interactions and interesting
conversations. There is a lot of interest and adoption of Nix, which is great to
see!</p>
<p>The production level of the conference was <em>very high</em> for such a
volunteer-driven event. For example, the very cool sounding break music between
talks was created specifically for NixCon: <a href="https://tonstrstudio.bandcamp.com/album/lava">“Lava” by
tonstr.studio</a>. Similarly, the
welcome bag contained dark Swiss chocolate, specifically made for NixCon (see
picture below). I don’t even like dark chocolate, but this one was delicious!</p>
<p>Thanks again to all helpers, and I look forward to coming back soon!</p>















<a href="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5755.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5755_hu_9e2fa5817975ea55.jpg 2x,https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5755_hu_8c2c2412f4af86af.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/IMG_5755_hu_8736ee28b37b1d95.jpg"
  alt="NixCon 2025 Swiss chocolate" title="NixCon 2025 Swiss chocolate"
  width="600"
  height="800"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[Secret Management on NixOS with sops-nix]]></title>
    <link href="https://michael.stapelberg.ch/posts/2025-08-24-secret-management-with-sops-nix/"/>
    <id>https://michael.stapelberg.ch/posts/2025-08-24-secret-management-with-sops-nix/</id>
    <published>2025-08-24T09:56:00+02:00</published>
    <content type="html"><![CDATA[<p>Passwords and secrets like cryptographic key files are everywhere in
computing. When configuring a Linux system, sooner or later you will need to put
a password somewhere — for example, when I <a href="/posts/2025-07-13-nixos-nas-network-storage-config/">migrated my existing Linux Network
Storage (NAS) setup to
NixOS</a>, I needed to specify
the desired Samba passwords in my NixOS config (or manage them manually, outside
of NixOS). For personal computers, this is fine, but if the goal is to share
system configurations (for example in a Git repository), we need a different
solution: Secret Management.</p>
<h2 id="what-is-secret-management">What is Secret Management?</h2>
<p>The basic idea behind Secret Management systems is to <em>encrypt</em> the secrets at
rest, meaning if somebody clones the git repository containing your NixOS system
configurations, they cannot access (and therefore, also not deploy) the
encrypted secrets.</p>
<p>Conceptually, we need to:</p>
<ol>
<li>Encrypt the secrets such that the target system can decrypt them.</li>
<li>Encrypt the secrets such that other people working on this config can decrypt
them.</li>
<li>Have the target system decrypt secrets at runtime.</li>
<li>Tell our software where to access the decrypted secrets.</li>
</ol>
<h2 id="sops-nix-setup">sops-nix setup</h2>
<p>In this article, I will show how to accomplish the above using sops-nix. Here’s
a quick overview of the three different building blocks we will use:</p>
<ul>
<li><a href="https://getsops.io/">sops</a> is a tool to version-control secrets in git, in
their encrypted form.
<ul>
<li>sops makes it easy to re-encrypt these secrets when adding/removing authorized keys.</li>
<li>sops is very flexible and can work with tons of other tools/providers.</li>
</ul>
</li>
<li><a href="https://github.com/Mic92/sops-nix">sops-nix</a> provides a way to integrate sops
with Nix/NixOS</li>
<li>Using sops with <a href="https://manpages.debian.org/age.1"><code>age(1)</code></a>
 allows us to use our
existing SSH private key (humans) or SSH host private key (machines) instead
of managing a separate set of key files.</li>
</ul>
<p>You might wonder why I chose sops-nix over
<a href="https://github.com/ryantm/agenix">agenix</a>, the other contender? The
instructions for setting up sops-nix made more sense to me when I first looked
at it, and I wanted to have the option to use sops in other ways, not just with
age. If you’re curious about agenix, <a href="https://www.splitbrain.org/blog/2025-07/27-agenix">check out Andreas Gohr’s blog post about
agenix</a>.</p>
<h3 id="step-1-preparation">Step 1. Preparation</h3>
<p>I ran the following instructions on an <a href="/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/#setup">Arch Linux machine on which I installed
the Nix tool and enabled Nix
Flakes</a>. Follow
the link for instructions also for other systems like Debian or Fedora.</p>
<h3 id="step-2-obtain-an-age-identity-from-your-personal-ssh-key">Step 2. Obtain an age identity from your personal SSH key</h3>
<p>I don’t want to manage an extra key file, so I’ll use <code>ssh-to-age</code> to derive a
key from my SSH private key file, which I already take good care of to back up:</p>
<pre tabindex="0"><code>midna % mkdir -p $HOME/.config/sops/age/
midna % read -s SSH_TO_AGE_PASSPHRASE; export SSH_TO_AGE_PASSPHRASE
midna % nix run nixpkgs#ssh-to-age -- \
  -private-key \
  -i $HOME/.ssh/id_ed25519 \
  -o $HOME/.config/sops/age/keys.txt
</code></pre><p>(The <code>SSH_TO_AGE_PASSPHRASE</code> option is documented in the <a href="https://github.com/Mic92/ssh-to-age/blob/main/README.md#usage">ssh-to-age
README</a>.)</p>
<p>To display the age recipient (public key) of this age identity (private key), I
used:</p>
<pre tabindex="0"><code>midna % nix shell nixpkgs#age
midna 2 % age-keygen -y $HOME/.config/sops/age/keys.txt
age10e9tt2qwq90y5hvl35dau0sm5cm4qvegtw2a70v7sz5fy99de42s9d5nkf
</code></pre><h3 id="step-3-obtain-an-age-recipient-for-the-remote-machine">Step 3. Obtain an age recipient for the remote machine</h3>
<p>Similarly, I will derive an age recipient from the SSH host key of the remote
system:</p>
<pre tabindex="0"><code>batchn % cat /etc/ssh/ssh_host_ed25519_key.pub | nix run nixpkgs#ssh-to-age
age1wnwfnrqhewjh39pmtyc8zhqw606znskt4h5p9s3pve4apd67gapqj6tr0k
</code></pre><h3 id="step-4-configure-sops-for-your-git-repository">Step 4. Configure sops for your git repository</h3>
<p>In my git repository (nix-configs), I have one subdirectory per NixOS system,
i.e. <a href="https://manpages.debian.org/tree.1"><code>tree(1)</code></a>
 shows:</p>
<pre tabindex="0"><code>├── batchn
│   ├── configuration.nix
│   ├── disk-config.nix
│   ├── flake.lock
│   ├── flake.nix
│   ├── hardware-configuration.nix
│   ├── Makefile
│   ├── secrets
│   │   └── example.yaml
├── wiki
│   ├── configuration.nix
│   ├── disk-config.nix
│   ├── flake.lock
│   ├── flake.nix
│   ├── hardware-configuration.nix
│   ├── Makefile
…
</code></pre><p>In the root of the git repository (next to the <code>batchn</code> directory), I create
<code>.sops.yaml</code> like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">keys</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#007020">&amp;admin_michael</span><span style="color:#bbb"> </span>age10e9tt2qwq90y5hvl35dau0sm5cm4qvegtw2a70v7sz5fy99de42s9d5nkf<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#007020">&amp;server_batchn</span><span style="color:#bbb"> </span>age1wnwfnrqhewjh39pmtyc8zhqw606znskt4h5p9s3pve4apd67gapqj6tr0k<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic"># …more server keys go here…</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">creation_rules</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">path_regex</span>:<span style="color:#bbb"> </span>batchn/secrets/[^/]+\.(yaml|json|env|ini)$<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">key_groups</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">age</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#007020">*admin_michael</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#007020">*server_batchn</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>The more systems I manage, the more <code>keys</code> and <code>creation_rules</code> I will need to
configure.</p>
<p>The creation rules tell sops which keys to use when encrypting a file. In my
setups, I typically use only a single file per system, but I could imagine
splitting out some secrets into a separate file if I wanted to collaborate with
someone on just one aspect of the system.</p>
<h3 id="step-5-manage-some-secrets-with-sops">Step 5. Manage some secrets with sops</h3>
<p>Now that we told sops which recipients to encrypt for, we can decrypt and edit
<code>secrets/example.yaml</code> in our configured editor by running:</p>
<pre tabindex="0"><code>midna ~/nix-configs/batchn % nix run nixpkgs#sops secrets/example.yaml
</code></pre><p>The simplest key file contains just one key, for example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">api-key</span>:<span style="color:#bbb"> </span>hello world :)<span style="color:#bbb">
</span></span></span></code></pre></div><p>After saving and exiting your editor, sops will update the encrypted
secrets/example.yaml.</p>
<h3 id="step-6-configure-sops-in-nixos">Step 6. Configure sops in NixOS</h3>
<p>Now, we need to reference the encrypted file in NixOS and enable <code>sops-nix</code>
integration to make the decrypted secrets available on the system.</p>
<p>In <code>flake.nix</code>, I added <code>sops-nix</code> to the <code>inputs</code> section and added the NixOS
module. I show the entire diff because the places where the lines go are just as
important as what the lines say:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#a00000">--- c/batchn/flake.nix
</span></span></span><span style="display:flex;"><span><span style="color:#a00000"></span><span style="color:#00a000">+++ i/batchn/flake.nix
</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span><span style="color:#800080;font-weight:bold">@@ -1,85 +1,93 @@
</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span> {
</span></span><span style="display:flex;"><span>   inputs = {
</span></span><span style="display:flex;"><span>     nixpkgs.url = &#34;github:nixos/nixpkgs/nixos-25.05&#34;;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     disko.url = &#34;github:nix-community/disko&#34;;
</span></span><span style="display:flex;"><span>     # Use the same version as nixpkgs
</span></span><span style="display:flex;"><span>     disko.inputs.nixpkgs.follows = &#34;nixpkgs&#34;;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     stapelbergnix.url = &#34;github:stapelberg/nix&#34;;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     zkjnastools.url = &#34;github:stapelberg/zkj-nas-tools&#34;;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#00a000">+    sops-nix = {
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+      url = &#34;github:Mic92/sops-nix&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+      inputs.nixpkgs.follows = &#34;nixpkgs&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+    };
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+
</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span>   };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   outputs =
</span></span><span style="display:flex;"><span>     {
</span></span><span style="display:flex;"><span>       nixpkgs,
</span></span><span style="display:flex;"><span>       disko,
</span></span><span style="display:flex;"><span>       stapelbergnix,
</span></span><span style="display:flex;"><span>       zkjnastools,
</span></span><span style="display:flex;"><span><span style="color:#00a000">+      sops-nix,
</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span>       ...
</span></span><span style="display:flex;"><span>     }:
</span></span><span style="display:flex;"><span>     let
</span></span><span style="display:flex;"><span>       system = &#34;x86_64-linux&#34;;
</span></span><span style="display:flex;"><span>       pkgs = import nixpkgs {
</span></span><span style="display:flex;"><span>         inherit system;
</span></span><span style="display:flex;"><span>         config.allowUnfree = false;
</span></span><span style="display:flex;"><span>       };
</span></span><span style="display:flex;"><span>     in
</span></span><span style="display:flex;"><span>     {
</span></span><span style="display:flex;"><span>       nixosConfigurations.batchn = nixpkgs.lib.nixosSystem {
</span></span><span style="display:flex;"><span>         inherit system;
</span></span><span style="display:flex;"><span>         inherit pkgs;
</span></span><span style="display:flex;"><span>         modules = [
</span></span><span style="display:flex;"><span>           disko.nixosModules.disko
</span></span><span style="display:flex;"><span>           ./configuration.nix
</span></span><span style="display:flex;"><span>           stapelbergnix.lib.userSettings
</span></span><span style="display:flex;"><span>           # Use systemd for network configuration
</span></span><span style="display:flex;"><span>           stapelbergnix.lib.systemdNetwork
</span></span><span style="display:flex;"><span>           # Use systemd-boot as bootloader
</span></span><span style="display:flex;"><span>           stapelbergnix.lib.systemdBoot
</span></span><span style="display:flex;"><span>           # Run prometheus node exporter in tailnet
</span></span><span style="display:flex;"><span>           stapelbergnix.lib.prometheusNode
</span></span><span style="display:flex;"><span>           zkjnastools.nixosModules.zkjbackup
</span></span><span style="display:flex;"><span><span style="color:#00a000">+          sops-nix.nixosModules.sops
</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span>         ];
</span></span><span style="display:flex;"><span>       };
</span></span><span style="display:flex;"><span>       formatter.${system} = pkgs.nixfmt-tree;
</span></span><span style="display:flex;"><span>     };
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><p>Then, in <code>configuration.nix</code>, we tell <code>sops-nix</code> to use the SSH host key as
identity, where sops will find our secrets and which secrets <code>sops-nix</code> should
realize on the remote system:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  sops<span style="color:#666">.</span>age<span style="color:#666">.</span>sshKeyPaths <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/etc/ssh/ssh_host_ed25519_key&#34;</span> ];
</span></span><span style="display:flex;"><span>  sops<span style="color:#666">.</span>defaultSopsFile <span style="color:#666">=</span> <span style="color:#235388">./secrets/example.yaml</span>;
</span></span><span style="display:flex;"><span>  sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;api-key&#34;</span> <span style="color:#666">=</span> { };
</span></span></code></pre></div><p>After deploying, we can access the secret on the running system:</p>
<pre tabindex="0"><code>batchn ~ % sudo cat /run/secrets/api-key
hello world :)%
batchn ~ %
</code></pre><p>Of course, even after rebooting the machine, the secrets remain available without a re-deploy:</p>
<pre tabindex="0"><code>batchn ~ % uptime
 22:09:23  up   0:00,  1 user,  load average: 0,32, 0,08, 0,03
batchn ~ % sudo cat /run/secrets/api-key
hello world :)%
batchn ~ %
</code></pre><h2 id="usage-examples">Usage Examples</h2>
<p>Now that we have secrets stored in files under <code>/run/secrets</code>, how can we use
these secrets?</p>
<p>The following sections show a few common ways.</p>
<h3 id="usage-example-command-line-flags-execstart-wrapper">Usage Example: command-line flags (ExecStart wrapper)</h3>
<p>Let’s assume you have deployed a custom Go server as a systemd service on NixOS
as follows, and you want to start managing the cleartext secret passed via the
<code>-securecookie_hash_key</code> and <code>-securecookie_block_key</code> command-line flags:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>groups<span style="color:#666">.</span>fortuneserver <span style="color:#666">=</span> { };
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>fortuneserver <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    isSystemUser <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    group <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>fortuneserver <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>    documentation <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;https://michael.stapelberg.ch&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;multi-user.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      User <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>      Group <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      ExecStart <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>fortuneserver<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/fortuneserver&#34; \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -securecookie_hash_key=&#34;some-secret-key&#34; \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -securecookie_block_key=&#34;a-different-secret-key&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>With the following sops secrets:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">fortuneserver</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">securecookie_hash_key</span>:<span style="color:#bbb"> </span>some-secret-key<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">securecookie_block_key</span>:<span style="color:#bbb"> </span>a-different-secret-key<span style="color:#bbb">
</span></span></span></code></pre></div><p>…we need to adjust our NixOS config to read these secret files at
runtime. Because the <code>ExecStart</code> directive is interpreted by systemd and not
passed through a shell, we use the <a href="https://nixos.org/manual/nixpkgs/stable/#trivial-builder-writeShellScript"><code>writeShellScript</code>
helper</a>
and then just <code>cat</code> the files:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;fortuneserver/securecookie_hash_key&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    owner <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    restartUnits <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;fortuneserver.service&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;fortuneserver/securecookie_block_key&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    owner <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    restartUnits <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;fortuneserver.service&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>groups<span style="color:#666">.</span>fortuneserver <span style="color:#666">=</span> { };
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>fortuneserver <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    isSystemUser <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    group <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>fortuneserver <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>    documentation <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;https://michael.stapelberg.ch&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;multi-user.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      User <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>      Group <span style="color:#666">=</span> <span style="color:#4070a0">&#34;fortuneserver&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      ExecStart <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>writeShellScript <span style="color:#4070a0">&#34;fortuneserver-execstart&#34;</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex; background-color:#d8d8d8"><span><span style="color:#4070a0">        &#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>fortuneserver<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/fortuneserver&#34; \
</span></span></span><span style="display:flex; background-color:#d8d8d8"><span><span style="color:#4070a0">          -securecookie_hash_key=&#34;$(cat /run/secrets/fortuneserver/securecookie_hash_key)&#34; \
</span></span></span><span style="display:flex; background-color:#d8d8d8"><span><span style="color:#4070a0">          -securecookie_block_key=&#34;$(cat /run/secrets/fortuneserver/securecookie_block_key)&#34;
</span></span></span><span style="display:flex; background-color:#d8d8d8"><span><span style="color:#4070a0">      &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<h3 id="usage-example-environment-variable-files">Usage Example: environment variable files</h3>
<p>What if the service in question does not use command-line flags, but environment
variables for configuring secrets? We can put an environment variable file into
a sops-managed secret:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">translate-fe</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">env</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        DEEPL_AUTH_KEY=my-deepl-key</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>…and then we make systemd apply these environment variables from the secrets file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;translate-fe/env&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    owner <span style="color:#666">=</span> <span style="color:#4070a0">&#34;translatefe&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    restartUnits <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;translate-fe.service&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>translate-fe <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    documentation <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;https://michael.stapelberg.ch&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;multi-user.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      User <span style="color:#666">=</span> <span style="color:#4070a0">&#34;translatefe&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      EnvironmentFile <span style="color:#666">=</span> [ config<span style="color:#666">.</span>sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;translate-fe/env&#34;</span><span style="color:#666">.</span>path ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      ExecStart <span style="color:#666">=</span> <span style="color:#4070a0">&#34;</span><span style="color:#70a0d0">${</span>translatefeExecstart<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/translate-fe&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>If you are configuring a NixOS module (instead of declaring a custom service),
the option might not always be called <code>EnvironmentFile</code>. For example, for the
oauth2-proxy service, you would need to configure the
<a href="https://search.nixos.org/options?channel=25.05&amp;show=services.oauth2-proxy.keyFile&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=oauth2-proxy"><code>services.oauth2-proxy.keyFile</code>
option</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  services<span style="color:#666">.</span>oauth2-proxy <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    keyFile <span style="color:#666">=</span> config<span style="color:#666">.</span>sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;oauth2-proxy/env&#34;</span><span style="color:#666">.</span>path;
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># …</span>
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><h3 id="usage-example-systemd-credentials">Usage Example: systemd credentials</h3>
<p>In the previous examples, we configured the <code>owner</code> of each secret to the user
account under which the service is running. But what if there is no such user
account, because the service use systemd’s <code>DynamicUser</code> feature?</p>
<p>We can use systemd’s <code>LoadCredential</code> feature! For example, I supply the SMTP
password to my Prometheus Alertmanager as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;alertmanager/smtp_pw&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    restartUnits <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;alertmanager.service&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>alertmanager<span style="color:#666">.</span>serviceConfig<span style="color:#666">.</span>LoadCredential <span style="color:#666">=</span> [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;smtp_pw:</span><span style="color:#70a0d0">${</span>config<span style="color:#666">.</span>sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;alertmanager/smtp_pw&#34;</span><span style="color:#666">.</span>path<span style="color:#70a0d0">}</span><span style="color:#4070a0">&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>prometheus<span style="color:#666">.</span>alertmanager <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    configuration <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      global <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>        smtp_smarthost <span style="color:#666">=</span> <span style="color:#4070a0">&#34;smtp.gmail.com:587&#34;</span>;
</span></span><span style="display:flex;"><span>        smtp_from <span style="color:#666">=</span> <span style="color:#4070a0">&#34;alerts@example.net&#34;</span>;
</span></span><span style="display:flex;"><span>        smtp_auth_username <span style="color:#666">=</span> <span style="color:#4070a0">&#34;alerts@example.net&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>        smtp_auth_password_file <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/run/credentials/alertmanager.service/smtp_pw&#34;</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#60a0b0;font-style:italic"># …remaining config goes here…</span>
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<h3 id="usage-example-samba-userspasswords">Usage Example: samba users/passwords</h3>
<p>In my blog post <a href="/posts/2025-07-13-nixos-nas-network-storage-config/#samba-nixos">“Migrating my NAS from CoreOS/Flatcar Linux to
NixOS”</a>, I
describe how to configure samba users and passwords (from sops-managed secrets)
with an <code>ExecStartPre</code> shell script (which is very similar to the techniques
already explained).</p>
<h2 id="conclusion">Conclusion</h2>
<p>Managing secrets as separately-encrypted files in your config repository makes
sense to me!</p>
<p>age’s ability to work with SSH keys makes for a really convenient setup, in my
opinion. Encrypting secrets for the destination system’s SSH host key feels very
elegant.</p>
<p>I hope the examples above are sufficient for you to efficiently configure
secrets in NixOS!</p>
]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[Development shells with Nix: four quick examples]]></title>
    <link href="https://michael.stapelberg.ch/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/"/>
    <id>https://michael.stapelberg.ch/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/</id>
    <published>2025-07-27T08:50:00+02:00</published>
    <content type="html"><![CDATA[<p>I wanted to use <a href="https://gocv.io/">GoCV</a> for one of my projects (to find and
extract paper documents from within a larger scan), without permanently having
OpenCV on my system.</p>
<p>This seemed like a good example use-case to demonstrate a couple of Nix commands
I like to use, covering quick interactive one-off dev shells to fully
declarative, hermetic, reproducible, shareable dev shells.</p>
<p>Notably, you don’t need to use NixOS to run these commands! You can <a href="/posts/2025-06-01-nixos-installation-declarative/#setup-nix">install and
use Nix</a> on any
Linux system like Debian, Arch, etc., as long as you set a Nix path or use
Flakes (see <a href="#setup">setup</a>).</p>
<h2 id="debian-way">For comparison: The Debian Way</h2>
<p>Before we start looking at Nix, I will show how to get GoCV running on Debian.</p>
<p>Let’s create a minimal Go program which uses a GoCV function like
<code>gocv.NewMat()</code>, just to verify that we can compile this program:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span>main<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;gocv.io/x/gocv&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">func</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>gocv.<span style="color:#06287e">NewMat</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>If we try to build this on a Debian system, we get:</p>
<pre tabindex="0"><code>debian % mkdir -p /tmp/minimal
debian % cd /tmp/minimal

debian % cat &gt; minimal.go &lt;&lt;&#39;EOT&#39;
package main
import &#34;gocv.io/x/gocv&#34;
func main() { gocv.NewMat(); }
EOT

debian % go mod init minimal
go: creating new go.mod: module minimal
go: to add module requirements and sums:
	go mod tidy

debian % go mod tidy
go: finding module for package gocv.io/x/gocv
go: downloading gocv.io/x/gocv v0.41.0
go: found gocv.io/x/gocv in gocv.io/x/gocv v0.41.0

debian % go build
# gocv.io/x/gocv
# [pkg-config --cflags  -- opencv4]
Package opencv4 was not found in the pkg-config search path.
Perhaps you should add the directory containing `opencv4.pc&#39;
to the PKG_CONFIG_PATH environment variable
Package &#39;opencv4&#39;, required by &#39;virtual:world&#39;, not found
</code></pre><p>On Debian, we can install OpenCV as follows:</p>
<pre tabindex="0"><code>debian % sudo apt install libopencv-dev

[…]

Summary:
  Upgrading: 7, Installing: 512, Removing: 0, Not Upgrading: 27
  Download size: 367 MB
  Space needed: 1590 MB / 281 GB available

Continue? [Y/n]
</code></pre><p>Saying “yes” to this prompt downloads and installs over 500 packages (takes a
few minutes).</p>
<p>Now the build works:</p>
<pre tabindex="0"><code>debian % go build
debian % file minimal
minimal: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), […]
</code></pre><p>…but we have over 500 extra packages on our system that will now need to be
updated in all eternity, therefore I would like to separate this one-off
experiment from my usual system.</p>
<p>We could use Docker to start a Debian container and work inside that container,
but, depending on the task, this can be cumbersome precisely because it’s a
separate environment. For this example, I would need to specify a volume mount
to make my input files available to the Docker container, and I would need to
set up environment variables before programs inside the Docker container can
open graphical windows on the host…</p>
<p>Let’s look at how we can use Nix to help us with that!</p>
<h2 id="setup">Setup: Nix-on-Debian (or Nix-on-Arch, or…)</h2>
<p>Users of NixOS can skip this section, as NixOS systems include a ready-to-use
Nix.</p>
<p>Before you can try the examples on your own computer, you need to complete these
three steps:</p>
<ol>
<li>Install Nix</li>
<li>Enable Flakes</li>
<li>Set a Nix path</li>
</ol>
<h3 id="setup-install">Step 1: Install Nix</h3>
<p>Users of Debian, Arch, Fedora, or other Linux systems first need to install
Nix. Luckily, Nix is available for many popular Linux distributions:</p>
<ul>
<li>Debian ships <a href="https://packages.debian.org/trixie/nix-setup-systemd">nix-setup-systemd</a></li>
<li>Arch Linux packages <a href="https://archlinux.org/packages/extra/x86_64/nix/">nix</a>
and provides documentation <a href="https://wiki.archlinux.org/title/Nix">on the Nix Arch Wiki
page</a>. In practice, I installed the
package and <a href="/posts/2025-06-01-nixos-installation-declarative/#setup-nix">configured a couple of <code>nixbld</code>
users</a>.</li>
<li>More generally, there are Nix builds (rpm, deb, pacman) available for a number
of distributions: <a href="https://github.com/nix-community/nix-installers">https://github.com/nix-community/nix-installers</a></li>
</ul>
<h3 id="setup-flakes">Step 2: Enable Flakes</h3>
<p>Nix flakes are <a href="https://determinate.systems/posts/flake-schemas/">“a generic way to package Nix
artifacts”</a>.</p>
<p>Examples 3 and 4 use Nix flakes to pin dependencies, so we need to <a href="/posts/2025-06-01-nixos-installation-declarative/#enabling-flakes">enable Nix
flakes</a>.</p>
<h3 id="setup-nix-path">Step 3: Set a Nix path</h3>
<p>For example 1 and 2, we want to use the Nix expression <code>import &lt;nixpkgs&gt;</code>.</p>
<p>On NixOS, this expression will follow the system version, meaning if you use
<code>import &lt;nixpkgs&gt;</code> on a NixOS 25.05 installation, that will reference <a href="https://github.com/NixOS/nixpkgs/tree/nixos-25.05/">nixpkgs
in version nixos-25.05</a>.</p>
<p>On other Linux systems, you’ll see an error message like this:</p>
<pre tabindex="0"><code>debian-server % nix-shell -p pkg-config opencv
error: file &#39;nixpkgs&#39; was not found in the Nix search path (add it using $NIX_PATH or -I)

       at «string»:1:25:

            1| {...}@args: with import &lt;nixpkgs&gt; args; (pkgs.runCommandCC or pkgs.runCommand) &#34;shell&#34; { buildInputs = [ (pkg-config) (opencv) ]; } &#34;&#34;
             |                         ^
(use &#39;--show-trace&#39; to show detailed location information)
</code></pre><p>We need to tell Nix which version of <code>nixpkgs</code> to use by setting the <a href="https://nixos.org/guides/nix-pills/15-nix-search-paths.html">Nix search
path</a>:</p>
<pre tabindex="0"><code>debian-server % export NIX_PATH=nixpkgs=channel:nixos-25.05
debian-server % nix-shell -p pkg-config opencv
[nix-shell:/tmp/opencv]#
</code></pre><p>Alright! Now we are set up. Let’s jump into the first example!</p>
<h2 id="nix-shell">Example 1: Interactive one-offs: nix-shell</h2>
<p>Nix provides a middle-ground between installing OpenCV on your system (<code>apt install</code> like in the example above) and installing OpenCV in a separate Docker
container: Nix can make available OpenCV without permanently installing it.</p>
<p>We can run <a href="https://manpages.debian.org/nix-shell.1"><code>nix-shell(1)</code></a>
 to start a bash shell in
which the specified packages are available. To successfully build Go code that
uses GoCV, we need to have OpenCV available:</p>
<pre tabindex="0"><code>% nix-shell -p pkg-config opencv
these 194 paths will be fetched (175.80 MiB download, 764.10 MiB unpacked):
  /nix/store/ig2nk0hsha9xaailhaj69yv677nv95q4-abseil-cpp-20210324.2
  /nix/store/yw5xqn8lqinrifm9ij80nrmf0i6fdcbx-alsa-lib-1.2.13
[…]

[nix-shell:/tmp/opencv]$ pkg-config --cflags opencv4
-I/nix/store/mh5b1dx2ifv4jkp9a8lgssxwhzxssb96-opencv-4.11.0/include/opencv4
</code></pre><p>In case you were wondering: Yes, we do need to specify <code>pkg-config</code> in this
<code>nix-shell</code> command explicitly, otherwise running <code>pkg-config</code> will run the host
version (outside the dev shell), which cannot find <code>opencv4.pc</code>.</p>
<h2 id="shell.nix">Example 2: nix-shell config file: shell.nix</h2>
<p>Once we have a combination of packages that work for our project (in our
example, just <code>pkg-config</code> and <code>opencv</code>), we can create a <code>shell.nix</code> (in any
directory, but usually in the root of a project) which <code>nix-shell</code> (without the
<code>-p</code> flag) will read:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  pkgs <span style="color:#666">?</span> <span style="color:#007020;font-weight:bold">import</span> <span style="color:#235388">&lt;nixpkgs&gt;</span> { }<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>}:
</span></span><span style="display:flex;"><span>pkgs<span style="color:#666">.</span>mkShell {
</span></span><span style="display:flex;"><span>  packages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># Explicitly list pkg-config so that mkShell will arrange</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># for the PKG_CONFIG_PATH to find the .pc files.</span>
</span></span><span style="display:flex;"><span>    pkg-config
</span></span><span style="display:flex;"><span>    opencv
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>…and then, we just run <code>nix-shell</code>:</p>
<pre tabindex="0"><code>% nix-shell
[nix-shell:/tmp/opencv]$ pkg-config --cflags opencv4
-I/nix/store/mh5b1dx2ifv4jkp9a8lgssxwhzxssb96-opencv-4.11.0/include/opencv4
</code></pre><p>If you’re curious, here are a couple of documentation pointers regarding the
boilerplate around the list of packages:</p>
<ul>
<li>Line 1 to 3 <a href="https://nixos.org/guides/nix-pills/05-functions-and-imports.html">declare a
function</a>
with an argument set — this is the required structure for <code>nix-shell</code> to be
able to call your <code>shell.nix</code> file.</li>
<li><a href="https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell"><code>pkgs.mkShell</code></a> is
a convenience helper for use with <code>nix-shell</code>.</li>
<li>The <code>with pkgs;</code> part allows us to write <code>opencv</code> instead of <code>pkgs.opencv</code>.</li>
</ul>
<p>By the way: With the <a href="https://github.com/nix-community/nixd">nixd language
server</a>, editors with <a href="https://en.wikipedia.org/wiki/Language_Server_Protocol">LSP
support</a> can show the
versions that packages resolve to, point out your spelling mistakes, or provide
features like “jump to definition”.</p>
<p>For example, in this screenshot, I was editing <code>shell.nix</code> in Emacs and was
curious how the Nix source of the <code>opencv</code> package looked like. By pressing
<code>M-.</code> (<code>xref-find-definitions</code>) with
<a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html">“point”</a>
over <code>opencv</code>, I got to <code>opencv/4.x.nix</code> in my local Nix store:</p>















<a href="https://michael.stapelberg.ch/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/2025-07-19-emacs-nix-shell.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/2025-07-19-emacs-nix-shell_hu_6ca6f897c44d3994.jpg 2x,https://michael.stapelberg.ch/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/2025-07-19-emacs-nix-shell_hu_ebe840dd6e5fb84c.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-07-27-dev-shells-with-nix-4-quick-examples/2025-07-19-emacs-nix-shell_hu_9fd651da07f25ae.jpg"
  alt="Emacs showing opencv/4.x.nix after jumping to definition of opencv" title="Emacs showing opencv/4.x.nix after jumping to definition of opencv"
  width="600"
  height="374"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<h2 id="nix-flakes">Example 3: Hermetic, pinned devShells: Nix Flakes</h2>
<p>The previous examples used nixpkgs from your system (or Nix path), which means
you don’t need to change the <code>.nix</code> file when you upgrade your system —
depending on the use-case, I see this behavior as either convenient or
terrifying.</p>
<p>For use-cases where it is important that the <code>.nix</code> file is built exactly the
same way, no matter what version the surrounding OS uses, we can use <a href="https://wiki.nixos.org/wiki/Flakes">Nix
Flakes</a> to build in a hermetic way, with
dependency versions pinned in the <code>flake.lock</code> file.</p>
<p>A <code>flake.nix</code> contains the same <code>mkShell</code> expression as above, but declares
structure around it: The <code>mkShell</code> expression goes into the
<code>outputs.devShells.x86_64-linux.default</code> attribute and the <code>inputs</code> attribute
contains <a href="https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-flake.html#flake-references">Flake
references</a>
that are available to this build:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs<span style="color:#666">.</span>nixpkgs<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:NixOS/nixpkgs/nixos-25.05&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    { self<span style="color:#666">,</span> nixpkgs }:
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      devShells<span style="color:#666">.</span>x86_64-linux<span style="color:#666">.</span>default <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>          pkgs <span style="color:#666">=</span> nixpkgs<span style="color:#666">.</span>legacyPackages<span style="color:#666">.</span>x86_64-linux;
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>        pkgs<span style="color:#666">.</span>mkShell {
</span></span><span style="display:flex;"><span>          packages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># Explicitly list pkg-config so that mkShell will arrange</span>
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># for the PKG_CONFIG_PATH to find the .pc files.</span>
</span></span><span style="display:flex;"><span>            pkg-config
</span></span><span style="display:flex;"><span>            opencv
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>By the way: Despite the name, it is a best practice to use
<code>nixpkgs.legacyPackages</code>, which conceptually provides a single <code>import nixpkgs</code>
result (<a href="https://discourse.nixos.org/t/using-nixpkgs-legacypackages-system-vs-import/17462/8">for
efficiency</a>).</p>
<p>Now, I can use <code>nix develop</code> to get a shell with OpenCV:</p>
<pre tabindex="0"><code>% nix develop
michael@midna$ pkg-config --cflags opencv4
-I/nix/store/mh5b1dx2ifv4jkp9a8lgssxwhzxssb96-opencv-4.11.0/include/opencv4
</code></pre><p>The first <code>nix develop</code> run creates a <code>flake.lock</code> file, so running <code>nix develop</code> later will get us exactly the same environment. To update to newer
versions, use <code>nix flake update</code>.</p>
<p><strong>Tip:</strong> Instead of a shell, <code>nix develop --command=emacs</code> is also a useful variant.</p>
<h2 id="system-indep-flake">Example 4: Making the Flake system-independent</h2>
<p>Unfortunately, the above <code>flake.nix</code> hard-codes <code>x86_64-linux</code>, so it will not
be usable on, say, an <code>aarch64-linux</code> (ARM) computer, or on a <code>x86_64-darwin</code>
(Mac).</p>
<p>Having to explicitly specify the <code>system</code> by default is a long-standing
criticism of Nix Flakes.</p>
<p>There are a number of workarounds. For example, we can use
<a href="https://github.com/numtide/flake-utils">numtide/flake-utils</a> and refactor our
<code>flake.nix</code> to use its
<a href="https://github.com/numtide/flake-utils?tab=readme-ov-file#eachdefaultsystem--system---attrs"><code>eachDefaultSystem</code></a>
convenience function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    nixpkgs<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nixos/nixpkgs/nixos-25.05&#34;</span>;
</span></span><span style="display:flex;"><span>    flake-utils<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:numtide/flake-utils&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      self<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      nixpkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      flake-utils<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>    }:
</span></span><span style="display:flex;"><span>    flake-utils<span style="color:#666">.</span>lib<span style="color:#666">.</span>eachDefaultSystem (
</span></span><span style="display:flex;"><span>      system:
</span></span><span style="display:flex;"><span>      <span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>        pkgs <span style="color:#666">=</span> nixpkgs<span style="color:#666">.</span>legacyPackages<span style="color:#666">.</span><span style="color:#70a0d0">${</span>system<span style="color:#70a0d0">}</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        formatter <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>nixfmt-tree;
</span></span><span style="display:flex;"><span>        devShells<span style="color:#666">.</span>default <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>mkShell {
</span></span><span style="display:flex;"><span>          packages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># Explicitly list pkg-config so that mkShell will arrange</span>
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic"># for the PKG_CONFIG_PATH to find the .pc files.</span>
</span></span><span style="display:flex;"><span>            pkg-config
</span></span><span style="display:flex;"><span>            opencv
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Or we could use <a href="https://github.com/numtide/blueprint">numtide/blueprint</a>,
its spiritual successor.</p>
<p>LucPerkins’s dev-templates <a href="https://github.com/the-nix-way/dev-templates/blob/main/go/flake.nix">have effectively
inlined</a> a
version of this technique.</p>
<p>For a solution that isn’t part of Nix, but Nix-adjacent:
<a href="https://devenv.sh/">devenv</a> is a separate tool that is built on Nix (no longer
using the CppNix implementation, but <a href="https://devenv.sh/blog/2024/10/22/devenv-is-switching-its-nix-implementation-to-tvix/">tvix
actually</a>),
but with its own .nix files.</p>
<h2 id="profile-install">Tip: Keeping packages around</h2>
<p>If you notice that <code>nix develop</code> or similar commands fetch packages despite the
<code>flake.lock</code> not having changed, you can install the Flake into your profile to
<a href="https://nixos.org/guides/nix-pills/11-garbage-collector.html">declare it as a gcroot to
Nix</a>:</p>
<pre tabindex="0"><code>% nix profile install .#devShells.x86_64-linux.default
</code></pre><p>But wait, isn’t that getting us into the same state as <a href="#debian-way">with The Debian
Way</a>? No! While OpenCV will remain available indefinitely if you
install the flake into your profile, there still is a layer of separation:
Within your system, OpenCV isn’t available, only when you start a development
shell with <code>nix-shell</code> or <code>nix develop</code>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>How do the four examples above compare? Here’s an overview:</p>
<table>
  <thead>
      <tr>
          <th>Example</th>
          <th>Boilerplate</th>
          <th>Pinned?</th>
          <th>System-dependent?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="#nix-shell">Ex 1</a>: <code>nix-shell -p …</code></td>
          <td>😊</td>
          <td>no</td>
          <td>no</td>
      </tr>
      <tr>
          <td><a href="#shell.nix">Ex 2</a>: <code>shell.nix</code></td>
          <td>🙂</td>
          <td>no</td>
          <td>no</td>
      </tr>
      <tr>
          <td><a href="#nix-flakes">Ex 3</a>: <code>flake.nix</code></td>
          <td>😲</td>
          <td>yes</td>
          <td>yes</td>
      </tr>
      <tr>
          <td><a href="#system-indep-flake">Ex 4</a>: system-independent <code>flake.nix</code></td>
          <td>🤨</td>
          <td>yes</td>
          <td>no</td>
      </tr>
  </tbody>
</table>
<p>For personal one-off experiments, I use <code>nix-shell</code>.</p>
<p>Once the experiment works, I typically want to pin the dependencies, so I use a
<code>flake.nix</code>.</p>
<p>If this is software that isn’t just versioned, but also published (or worked on
with multiple people/systems), I go through the effort of making it a
system-independent <code>flake.nix</code>.</p>
<p>I hope in the future, it will become easier to write a system-independent flake.</p>
<p>Despite the rough edges, I appreciate the reproducibility and control that Nix
gives me!</p>
]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[Migrating my NAS from CoreOS/Flatcar Linux to NixOS]]></title>
    <link href="https://michael.stapelberg.ch/posts/2025-07-13-nixos-nas-network-storage-config/"/>
    <id>https://michael.stapelberg.ch/posts/2025-07-13-nixos-nas-network-storage-config/</id>
    <published>2025-07-13T08:17:00+02:00</published>
    <content type="html"><![CDATA[<p>In this article, I want to show how to migrate an existing Linux server to NixOS
— in my case the CoreOS/Flatcar Linux installation on my Network Attached
Storage (NAS) PC.</p>
<p>I will show in detail how the previous CoreOS setup looked like (lots of systemd
units starting Docker containers), how I migrated it into an intermediate state
(using Docker on NixOS) just to get things going, and finally how I migrated all
units from Docker to native NixOS modules step-by-step.</p>
<p>If you haven’t heard of NixOS, I recommend you read the <a href="https://nixos.org">first page of the NixOS
website</a> to understand what NixOS is and what sort of things
it makes possible.</p>
<p>The target audience of this blog post is people interested in trying out NixOS
for the use-case of a NAS, who like seeing examples to understand how to
configure a system.</p>
<p>You can apply these examples by first following <a href="/posts/2025-06-01-nixos-installation-declarative/">my blog post “How I like to
install NixOS
(declaratively)”</a>, then
making your way through the sections that interest you. If you prefer seeing the
full configuration, <a href="#conclusion">skip to the conclusion</a>.</p>















<a href="https://michael.stapelberg.ch/posts/2025-07-13-nixos-nas-network-storage-config/IMG_5563.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-07-13-nixos-nas-network-storage-config/IMG_5563_hu_90f92def47078a68.jpg 2x,https://michael.stapelberg.ch/posts/2025-07-13-nixos-nas-network-storage-config/IMG_5563_hu_fbda205f2b2f2fa9.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-07-13-nixos-nas-network-storage-config/IMG_5563_hu_dfffd317a819e211.jpg"
  alt="PC NAS build from 2023" title="PC NAS build from 2023"
  width="600"
  height="479"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<h2 id="history">Context/History</h2>
<p>Over the last decade, I used a number of different operating systems for my
NAS needs. Here’s an overview of the 2 NAS systems storage2 and storage3:</p>
<table>
  <thead>
      <tr>
          <th>Year</th>
          <th>storage2</th>
          <th>storage3</th>
          <th>Details (blog post)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>2013</td>
          <td>Debian on qnap</td>
          <td>Debian on qnap</td>
          <td><a href="/posts/2014-01-28-qnap_ts119_wol/">Wake-On-LAN with Debian on a qnap TS-119P2+</a></td>
      </tr>
      <tr>
          <td>2016</td>
          <td>CoreOS on PC</td>
          <td>CoreOS on PC</td>
          <td><a href="/posts/2016-11-21-gigabit-nas-coreos/">Gigabit NAS (running CoreOS)</a></td>
      </tr>
      <tr>
          <td>2023</td>
          <td>CoreOS on PC</td>
          <td>Ubuntu+ZFS on PC</td>
          <td><a href="/posts/2023-10-25-my-all-flash-zfs-network-storage-build/">My all-flash ZFS NAS build</a></td>
      </tr>
      <tr>
          <td>2025</td>
          <td>NixOS on PC</td>
          <td>Ubuntu+ZFS on PC</td>
          <td>→ you are here ←</td>
      </tr>
      <tr>
          <td>?</td>
          <td>NixOS on PC</td>
          <td>NixOS+ZFS on PC</td>
          <td>Converting more PCs to NixOS seems inevitable ;)</td>
      </tr>
  </tbody>
</table>
<h2 id="software-requirements">My NAS Software Requirements</h2>
<ul>
<li>(This post is only about software! For my usage patterns and requirements
regarding hardware selection, see <a href="/posts/2023-10-25-my-all-flash-zfs-network-storage-build/#design-goals">“Design Goals” in my My all-flash ZFS NAS
build post
(2023)</a>.)</li>
<li><strong>Remote management:</strong> I really like the model of having the configuration of
my network storage builds version-controlled and managed on my main PC. It’s a
nice property that I can regain access to my backup setup by re-installing my
NAS from my PC within minutes.</li>
<li><strong>Automated updates, with easy rollback:</strong> Updating all my installations
manually is not my idea of a good time. Hence, automated updates are a must —
but when the update breaks, a quick and easy path to recovery is also a
must.
<ul>
<li>CoreOS/Flatcar achieved that with an A/B updating scheme (update failed?
boot the old partition), whereas NixOS achieves that with its concept of a
“generation” (update failed? select the old generation), which is
finer-grained.</li>
</ul>
</li>
</ul>
<h2 id="why-migrate">Why migrate from CoreOS/Flatcar to NixOS?</h2>
<p>When I started using CoreOS, Docker was pretty new technology. I liked that
using Docker containers allowed you to treat services uniformly — ultimately,
they all expose a port of some sort (speaking HTTP, or Postgres, or…), so you
got the flexibility to run much more recent versions of software on a stable OS,
or older versions in case an update broke something.</p>
<p>Over a decade later, Docker is established tech. People nowadays take for
granted the various benefits of the container approach.</p>
<p>So, here’s my list of reasons why I wasn’t satisfied with Flatcar Linux anymore.</p>
<h4 id="cloud-init">R1. cloud-init is deprecated</h4>
<p>The <a href="https://github.com/coreos/coreos-cloudinit">CoreOS cloud-init</a> project was
deprecated at some point in favor of
<a href="https://github.com/coreos/ignition">Ignition</a>, which is clearly more powerful,
but also more cumbersome to get started with as a hobbyist. As far as I can
tell, I must host my config at some URL that I then provide via a kernel
parameter. The old way of just copying a file seems to no longer be supported.</p>
<p>Ignition also seems less convenient in other ways: YAML is no longer supported,
only JSON, which I don’t enjoy writing by hand. Also, the format seems to
<a href="https://coreos.github.io/ignition/migrating-configs/">change quite a bit</a>.</p>
<p>As a result, I never made the jump from cloud-init to Ignition, and it’s not
good to be reliant on a long-deprecated way to use your OS of choice.</p>
<h4 id="container-bitrot">R2. Container Bitrot</h4>
<p>At some point, I did an audit of all my containers on the Docker Hub and noticed
that most of them were quite outdated. For a while, Docker Hub offered automated
builds based on a <code>Dockerfile</code> obtained from GitHub. However, automated builds
now require a subscription, and I will not accept a subscription just to use my
own computers.</p>
<h4 id="r3-dependency-on-a-central-service">R3. Dependency on a central service</h4>
<p>If Docker at some point ceases operation of the Docker Hub, I am unable to
deploy software to my NAS. This isn’t a very hypothetical concern: In 2023,
Docker Hub <a href="https://news.ycombinator.com/item?id=35154025">announced the end of organizations on the Free
tier</a> and then backpedaled after
community backlash.</p>
<p>Who knows how long they can still provide free services to hobbyists like myself.</p>
<h4 id="no-immich">R4. Could not try Immich on Flatcar</h4>
<p>The final nail in the coffin was when I noticed that I could not try Immich on
my NAS system! Modern web applications like Immich need multiple Docker
containers (for Postgres, Redis, etc.) and hence only offer <a href="https://immich.app/docs/install/docker-compose">Docker
Compose</a> as a supported way of
installation.</p>
<p>Unfortunately, Flatcar <a href="https://github.com/flatcar/Flatcar/issues/894">does not include Docker
Compose</a>.</p>
<p>I was not in the mood to re-package Immich for non-Docker-Compose systems on an
ongoing basis, so I decided that a system on which I can neither run software
like Immich directly, nor even run Docker Compose, is not sufficient for my
needs anymore.</p>
<h4 id="reason-summary">Reason Summary</h4>
<p>With all of the above reasons, I would have had to set up automated container
builds, run my own central registry and would still be unable to run well-known
Open Source software like Immich.</p>
<p>Instead, I decided to try NixOS again (after a 10 year break) because it seems
like the most popular declarative solution nowadays, with a large community and
large selection of packages.</p>
<p>How does NixOS compare for my situation?</p>
<ul>
<li>Same: I also need to set up an automated job to update my NixOS systems.
<ul>
<li>I already have such a job for updating my <a href="https://gokrazy.org">gokrazy</a> devices.</li>
<li>Docker push is asynchronous: After a successful push, I still need extra
automation for pulling the updated containers on the target host and
restarting the affected services, whereas NixOS includes all of that.</li>
</ul>
</li>
<li>Better: There is no central registry. With NixOS, I can push the build result
directly to the target host via SSH.</li>
<li>Better: The corpus of available software in NixOS is much larger (including
Immich, for example) and the NixOS modules generally seem to be expressed at a
higher level of abstraction than individual Docker containers, meaning you can
configure more features with fewer lines of config.</li>
</ul>
<h2 id="vm-prototyping">Prototyping in a VM</h2>
<p>My NAS setup needs to work every day, so I wanted to prototype my desired
configuration in a VM before making changes to my system. This is not only
safer, it also allows me to discover any roadblocks, and what working with NixOS
feels like without making any commitments.</p>
<p>I copied my NixOS configuration from a previous test installation (see <a href="/posts/2025-06-01-nixos-installation-declarative/">“How I
like to install NixOS
(declaratively)”</a>) and used
the following command to build a VM image and start it in QEMU:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>nix build .#nixosConfigurations.storage2.config.system.build.vm
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">QEMU_NET_OPTS</span><span style="color:#666">=</span><span style="color:#bb60d5">hostfwd</span><span style="color:#666">=</span>tcp::2222-:22
</span></span><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">QEMU_KERNEL_PARAMS</span><span style="color:#666">=</span><span style="color:#bb60d5">console</span><span style="color:#666">=</span>ttyS0
</span></span><span style="display:flex;"><span>./result/bin/run-nixplay-vm
</span></span></code></pre></div><p>The configuration instructions below can be tried out in this VM, and once
you’re happy enough with what you have, you can repeat the steps on the actual
machine to migrate.</p>
<h2 id="migrating">Migrating</h2>
<p>For the migration of my actual system, I defined the following milestones that
should be achievable within a typical session of about an hour (after
prototyping them in a VM):</p>
<ul>
<li>M1. Install NixOS</li>
<li>M2. Set up remote disk unlock</li>
<li>M3. Set up Samba for access</li>
<li>M4. Set up SSH/rsync for backups</li>
<li>Everything extra is nice-to-have and could be deferred to a future session on
another day.</li>
</ul>
<p>In practice, this worked out exactly as planned: the actual installation of
NixOS and setting up my config to milestone M4 took a little over one hour. All
the other nice-to-haves were done over the following days and weeks as time
permitted.</p>
<p><strong>Tip:</strong> After losing data due to an installer bug in the 2000s, I have adopted
the habit of physically disconnecting all data disks (= pulling out the SATA
cable) when re-installing the system disk.</p>
<h3 id="m1-install-nixos">M1. Install NixOS</h3>
<p>After following <a href="/posts/2025-06-01-nixos-installation-declarative/">“How I like to install NixOS
(declaratively)”</a>, this is
my initial <code>configuration.nix</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ modulesPath<span style="color:#666">,</span> lib<span style="color:#666">,</span> pkgs<span style="color:#666">,</span> <span style="color:#666">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  imports <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    [
</span></span><span style="display:flex;"><span>      (modulesPath <span style="color:#666">+</span> <span style="color:#4070a0">&#34;/installer/scan/not-detected.nix&#34;</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#235388">./hardware-configuration.nix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#235388">./disk-config.nix</span>
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  <span style="color:#60a0b0;font-style:italic"># Adding michael as trusted user means</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  <span style="color:#60a0b0;font-style:italic"># we can upgrade the system via SSH (see Makefile).</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  nix<span style="color:#666">.</span>settings<span style="color:#666">.</span>trusted-users <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;michael&#34;</span> <span style="color:#4070a0">&#34;root&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  <span style="color:#60a0b0;font-style:italic"># Clean the Nix store every week.</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  nix<span style="color:#666">.</span>gc <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    automatic <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    dates <span style="color:#666">=</span> <span style="color:#4070a0">&#34;weekly&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    options <span style="color:#666">=</span> <span style="color:#4070a0">&#34;--delete-older-than 7d&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  boot<span style="color:#666">.</span>loader<span style="color:#666">.</span>systemd-boot <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    configurationLimit <span style="color:#666">=</span> <span style="color:#40a070">10</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  boot<span style="color:#666">.</span>loader<span style="color:#666">.</span>efi<span style="color:#666">.</span>canTouchEfiVariables <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  networking<span style="color:#666">.</span>hostName <span style="color:#666">=</span> <span style="color:#4070a0">&#34;storage2&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  time<span style="color:#666">.</span>timeZone <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Europe/Zurich&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  <span style="color:#60a0b0;font-style:italic"># Use systemd for networking</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  services<span style="color:#666">.</span>resolved<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  networking<span style="color:#666">.</span>useDHCP <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>networks<span style="color:#666">.</span><span style="color:#4070a0">&#34;10-e&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    matchConfig<span style="color:#666">.</span>Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;e*&#34;</span>;  <span style="color:#60a0b0;font-style:italic"># enp9s0 (10G) or enp8s0 (1G)</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    networkConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      IPv6AcceptRA <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      DHCP <span style="color:#666">=</span> <span style="color:#4070a0">&#34;yes&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  i18n<span style="color:#666">.</span>supportedLocales <span style="color:#666">=</span> [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;en_DK.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;de_DE.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;de_CH.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;en_US.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  i18n<span style="color:#666">.</span>defaultLocale <span style="color:#666">=</span> <span style="color:#4070a0">&#34;en_US.UTF-8&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  users<span style="color:#666">.</span>mutableUsers <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  security<span style="color:#666">.</span>sudo<span style="color:#666">.</span>wheelNeedsPassword <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      <span style="color:#4070a0">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5secret&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      <span style="color:#4070a0">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5key&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    isNormalUser <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Michael Stapelberg&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    extraGroups <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;networkmanager&#34;</span> <span style="color:#4070a0">&#34;wheel&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    initialPassword <span style="color:#666">=</span> <span style="color:#4070a0">&#34;secret&#34;</span>;  <span style="color:#60a0b0;font-style:italic"># XXX: change!</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    shell <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>zsh;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    packages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    git  <span style="color:#60a0b0;font-style:italic"># for checking out github.com/stapelberg/configfiles</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    rsync
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    zsh
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    vim
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    emacs
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    wget
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    curl
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  programs<span style="color:#666">.</span>zsh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  services<span style="color:#666">.</span>openssh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># This value determines the NixOS release from which the default</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># settings for stateful data, like file locations and database versions</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># on your system were taken. It‘s perfectly fine and recommended to leave</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># this value at the release version of the first install of this system.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Before changing this value read the documentation for this option</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).</span>
</span></span><span style="display:flex;"><span>  system<span style="color:#666">.</span>stateVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#34;25.05&#34;</span>; <span style="color:#60a0b0;font-style:italic"># Did you read the comment?</span>
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>All following sections describe changes within this <code>configuration.nix</code>.</p>
<p>All devices in my home network obtain their IP address via DHCP. If I want to
make an IP address static, I configure it accordingly on my router.</p>
<p>My NAS PCs have one specialty with regards to IP addressing: They are reachable
via IPv4 and IPv6, and the IPv6 address can be derived from the IPv4 address.</p>
<p>Hence, I changed the systemd-networkd configuration from above such that it
configures a static IPv6 address in a dynamically configured IPv6 network:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>networks<span style="color:#666">.</span><span style="color:#4070a0">&#34;10-e&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    matchConfig<span style="color:#666">.</span>Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;e*&#34;</span>;  <span style="color:#60a0b0;font-style:italic"># enp9s0 (10G) or enp8s0 (1G)</span>
</span></span><span style="display:flex;"><span>    networkConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      IPv6AcceptRA <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>      DHCP <span style="color:#666">=</span> <span style="color:#4070a0">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    ipv6AcceptRAConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      Token <span style="color:#666">=</span> <span style="color:#4070a0">&#34;::10:0:0:252&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    };
</span></span><span style="display:flex;"><span>  };</span></span></code></pre></div>
<p>✅ This fulfills milestone M1.</p>
<h3 id="m2-set-up-remote-disk-unlock">M2. Set up remote disk unlock</h3>
<p>To unlock my encrypted disks on boot, I have a custom systemd service unit that
uses <a href="https://manpages.debian.org/wget.1"><code>wget(1)</code></a>
 and <a href="https://manpages.debian.org/cryptsetup.8"><code>cryptsetup(8)</code></a>
 to split the key file between the NAS and a remote server (= an
attacker needs both pieces to unlock).</p>
<p>With CoreOS/Flatcar, my <code>cloud-init</code> configuration looked as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">coreos</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">units</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>unlock.service<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">command</span>:<span style="color:#bbb"> </span>start<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        [Unit]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Description=unlock hard drive
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Wants=network.target
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        After=systemd-networkd-wait-online.service
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Before=samba.service
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        [Service]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Type=oneshot
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        RemainAfterExit=yes
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        # Wait until the host is actually reachable.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/bin/sh -c &#34;c=0; while [ $c -lt 5 ]; do /bin/ping6 -n -c 1 r.zekjur.net &amp;&amp; break; c=$((c+1)); sleep 1; done&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/bin/sh -c &#34;[ -e \&#34;/dev/mapper/S5SSNF0T205183F_crypt\&#34; ] || (echo -n my_local_secret &amp;&amp; wget --retry-connrefused --ca-directory=/dev/null --ca-certificate=/etc/ssl/certs/r.zekjur.net.crt -qO - https://r.zekjur.net:8443/nascrypto) | /sbin/cryptsetup --key-file=- luksOpen /dev/disk/by-id/ata-Samsung_SSD_870_QVO_8TB_S5SSNF0T205183F S5SSNF0T205183F_crypt&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/bin/sh -c &#34;[ -e \&#34;/dev/mapper/S5SSNJ0T205991B_crypt\&#34; ] || (echo -n my_local_secret &amp;&amp; wget --retry-connrefused --ca-directory=/dev/null --ca-certificate=/etc/ssl/certs/r.zekjur.net.crt -qO - https://r.zekjur.net:8443/nascrypto) | /sbin/cryptsetup --key-file=- luksOpen /dev/disk/by-id/ata-Samsung_SSD_870_QVO_8TB_S5SSNJ0T205991B S5SSNJ0T205991B_crypt&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/bin/sh -c &#34;vgchange -ay&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/bin/mount /dev/mapper/data-data /srv</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">write_files</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">path</span>:<span style="color:#bbb"> </span>/etc/ssl/certs/r.zekjur.net.crt<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">      -----BEGIN CERTIFICATE-----
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">      MIID8TCCAlmgAwIBAgIRAPWwvYWpoH+lGKv6rxZvC4MwDQYJKoZIhvcNAQELBQAw
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">      […]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">      -----END CERTIFICATE-----</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>I converted it into the following NixOS configuration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>unlock <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;multi-user.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;unlock hard drive&#34;</span>;
</span></span><span style="display:flex;"><span>    wants <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;network.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    after <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;systemd-networkd-wait-online.service&#34;</span> ];
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      Type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;oneshot&#34;</span>;
</span></span><span style="display:flex;"><span>      RemainAfterExit <span style="color:#666">=</span> <span style="color:#4070a0">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>      ExecStart <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># Wait until the host is actually reachable.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;/bin/sh -c &#34;c=0; while [ $c -lt 5 ]; do </span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>iputils<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/ping -n -c 1 r.zekjur.net &amp;&amp; break; c=$((c+1)); sleep 1; done&#34;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;/bin/sh -c &#34;[ -e \&#34;/dev/mapper/S5SSNF0T205183F_crypt\&#34; ] || (echo -n my_local_secret &amp;&amp; </span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>wget<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/wget --retry-connrefused --ca-directory=/dev/null --ca-certificate=/etc/ssl/certs/r.zekjur.net.crt -qO - https://r.zekjur.net:8443/sdb2_crypt) | </span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>cryptsetup<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/cryptsetup --key-file=- luksOpen /dev/disk/by-id/ata-Samsung_SSD_870_QVO_8TB_S5SSNF0T205183F S5SSNF0T205183F_crypt&#34;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;/bin/sh -c &#34;[ -e \&#34;/dev/mapper/S5SSNJ0T205991B_crypt\&#34; ] || (echo -n my_local_secret &amp;&amp; </span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>wget<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/wget --retry-connrefused --ca-directory=/dev/null --ca-certificate=/etc/ssl/certs/r.zekjur.net.crt -qO - https://r.zekjur.net:8443/sdc2_crypt) | </span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>cryptsetup<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/cryptsetup --key-file=- luksOpen /dev/disk/by-id/ata-Samsung_SSD_870_QVO_8TB_S5SSNJ0T205991B S5SSNJ0T205991B_crypt&#34;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;/bin/sh -c &#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>lvm2<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/vgchange -ay&#34;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;/run/wrappers/bin/mount /dev/mapper/data-data /srv&#39;&#39;</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>We’ll also need to store the custom TLS certificate file on disk. For that, we
can use the <code>environment.</code> configuration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  environment<span style="color:#666">.</span>etc<span style="color:#666">.</span><span style="color:#4070a0">&#34;ssl/certs/r.zekjur.net.crt&#34;</span><span style="color:#666">.</span>text <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">-----BEGIN CERTIFICATE-----
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">MIID8TCCAlmgAwIBAgIRAPWwvYWpoH+lGKv6rxZvC4MwDQYJKoZIhvcNAQELBQAw
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">[…]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">-----END CERTIFICATE-----
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">&#39;&#39;</span>;
</span></span></code></pre></div><p>The references like <code>${pkgs.wget}</code> will be replaced with a path to the Nix store
(<a href="https://nix.dev/tutorials/nix-language.html#paths">→ nix.dev
documentation</a>). On
CoreOS/Flatcar, I was limited to using just the (minimal set of) software
included in the base image, or I had to reach for Docker. On NixOS, we can use
all packages available in nixpkgs.</p>
<p>After <a href="/posts/2025-06-01-nixos-installation-declarative/#making-changes">deploying</a>
and <code>reboot</code>ing, I can access my unlocked disk under <code>/srv</code>! 🎉</p>
<pre tabindex="0"><code>% df -h /srv
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/data-data   15T   14T  342G  98% /srv
</code></pre><p>When listing my files, I noticed that the group id was different between my old
system and the new system. This can be fixed by explicitly specifying the
desired group id:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  users<span style="color:#666">.</span>groups<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    gid <span style="color:#666">=</span> <span style="color:#40a070">1000</span>;  <span style="color:#60a0b0;font-style:italic"># for consistency with storage3</span>
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>✅ M2 is complete.</p>
<h3 id="m3-set-up-samba-for-access">M3. Set up Samba for access</h3>
<p>Whereas I want to configure remote disk unlock at the systemd service level, for
Samba I want to use Docker: I wanted to first transfer my old (working)
Docker-based setups as they are, and only later convert them to Nix.</p>
<p>We enable the <a href="https://search.nixos.org/options?query=virtualisation.docker.enable">Docker NixOS
module</a>
which sets up the daemons that Docker needs and whatever else is needed to make
it work:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  virtualisation<span style="color:#666">.</span>docker<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span></code></pre></div><p>This is already sufficient for other services to use Docker, but I also want to
be able to run the <code>docker</code> command interactively for debugging. Therefore, I
added <code>docker</code> to <code>systemPackages</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex;"><span>    git  <span style="color:#60a0b0;font-style:italic"># for checking out github.com/stapelberg/configfiles</span>
</span></span><span style="display:flex;"><span>    rsync
</span></span><span style="display:flex;"><span>    zsh
</span></span><span style="display:flex;"><span>    vim
</span></span><span style="display:flex;"><span>    emacs
</span></span><span style="display:flex;"><span>    wget
</span></span><span style="display:flex;"><span>    curl
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    docker
</span></span><span style="display:flex;"><span>  ];</span></span></code></pre></div>
<p>After deploying this configuration, I can run <code>docker run -ti debian</code> to verify things work.</p>
<p>The <code>cloud-init</code> version of samba looked like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">coreos</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">units</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>samba.service<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">command</span>:<span style="color:#bbb"> </span>start<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        [Unit]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Description=samba server
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        After=docker.service unlock.mount
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Requires=docker.service unlock.mount
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        [Service]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Restart=always
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        StartLimitInterval=0
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        # Always pull the latest version (bleeding edge).
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=-/usr/bin/docker pull stapelberg/docker-samba:latest
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        # Set up samba users (cannot be done in the (public) Dockerfile because
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        # users/passwords are sensitive information).
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=-/usr/bin/docker kill smb
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=-/usr/bin/docker rm smb
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=-/usr/bin/docker rm smb-prep
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/usr/bin/docker run --name smb-prep stapelberg/docker-samba sh -c &#39;adduser --quiet --disabled-password --gecos &#34;&#34; --uid 29901 michael &amp;&amp; sed -i &#34;s,\\[global\\],[global]\\nserver multi channel support = yes\\naio read size = 1\\naio write size = 1,g&#34; /etc/samba/smb.conf&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/usr/bin/docker commit smb-prep smb-prepared
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/usr/bin/docker rm smb-prep
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/usr/bin/docker run --name smb-prep smb-prepared /bin/sh -c &#34;echo \&#34;secret\nsecret\n&#34; | tee - | smbpasswd -a -s michael&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/usr/bin/docker commit smb-prep smb-prepared
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/usr/bin/docker run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          -p 137:137 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          -p 138:138 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          -p 139:139 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          -p 445:445 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          --tmpfs=/run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          -v /srv/data:/srv/data \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          --name smb \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          -t \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          smb-prepared \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">            /usr/sbin/smbd --foreground --debug-stdout --no-process-group</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>We can translate this 1:1 to NixOS:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>samba <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;multi-user.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;samba server&#34;</span>;
</span></span><span style="display:flex;"><span>    after <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;unlock.service&#34;</span> ];
</span></span><span style="display:flex;"><span>    requires <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;unlock.service&#34;</span> ];
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      Restart <span style="color:#666">=</span> <span style="color:#4070a0">&#34;always&#34;</span>;
</span></span><span style="display:flex;"><span>      StartLimitInterval <span style="color:#666">=</span> <span style="color:#40a070">0</span>;
</span></span><span style="display:flex;"><span>      ExecStartPre <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># Always pull the latest version.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker pull stapelberg/docker-samba:latest&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># Set up samba users (cannot be done in the (public) Dockerfile because</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic"># users/passwords are sensitive information).</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker kill smb&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker rm smb&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker rm smb-prep&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run --name smb-prep stapelberg/docker-samba sh -c &#39;adduser --quiet --disabled-password --gecos &#34;&#34; --uid 29901 michael &amp;&amp; sed -i &#34;s,\\[global\\],[global]\\nserver multi channel support = yes\\naio read size = 1\\naio write size = 1,g&#34; /etc/samba/smb.conf&#39; &#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker commit smb-prep smb-prepared&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker rm smb-prep&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run --name smb-prep smb-prepared /bin/sh -c &#34;echo \&#34;secret\nsecret\n&#34; | tee - | smbpasswd -a -s michael&#34;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker commit smb-prep smb-prepared&#39;&#39;</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      ExecStart <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           -p 137:137 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           -p 138:138 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           -p 139:139 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           -p 445:445 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           --tmpfs=/run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           -v /srv/data:/srv/data \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           --name smb \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           -t \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           smb-prepared \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">             /usr/sbin/smbd --foreground --debug-stdout --no-process-group
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">             &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span><span style="">}</span>
</span></span></code></pre></div><p>✅ Now I can manage my files over the network, which completes M3!</p>
<p>See also: <a href="#samba-nixos">Nice-to-haves: N5. samba from NixOS</a></p>
<h3 id="m4-set-up-sshrsync-for-backups">M4. Set up SSH/rsync for backups</h3>
<p>For backing up data, I use rsync over SSH. I restrict this SSH access to run
only rsync commands by using <code>rrsync</code> (in a Docker container). To configure the
SSH <a href="https://manpages.debian.org/authorized_keys.5"><code>authorized_keys(5)</code></a>
, we set:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>root<span style="color:#666">.</span>openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;&#39;command=&#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run --log-driver none -i -e SSH_ORIGINAL_COMMAND -v /srv/backup/midna:/srv/backup/midna stapelberg/docker-rsync /srv/backup/midna&#34; ssh-rsa AAAAB3Npublickey root@midna&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="">}</span>;
</span></span></code></pre></div><p>✅ A successful test backup run completes milestone M4!</p>
<p>See also: <a href="#rrsync-nixos">Nice-to-haves: N6. rrsync from NixOS</a></p>
<h2 id="nice-to-haves">Nice-to-haves</h2>
<h3 id="prometheus-node-exporter">N1. Prometheus Node Exporter</h3>
<p>I like to monitor all my machines with <a href="https://prometheus.io">Prometheus</a> (and
Grafana). For network connectivity and authentication, I use the Tailscale mesh
VPN.</p>
<p>To install Tailscale, I <a href="https://search.nixos.org/options?query=services.tailscale.enable">enable its NixOS
module</a> and
make the <code>tailscale</code> command available:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  services<span style="color:#666">.</span>tailscale<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [ tailscale ];
</span></span></code></pre></div><p>After deploying, I run <code>sudo tailscale up</code> and open the login link in my browser.</p>
<p>The Prometheus Node Exporter can also easily be enabled <a href="https://search.nixos.org/options?query=services.prometheus.exporters.node.enable">through its NixOS
module</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  services<span style="color:#666">.</span>prometheus<span style="color:#666">.</span>exporters<span style="color:#666">.</span>node <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    listenAddress <span style="color:#666">=</span> <span style="color:#4070a0">&#34;storage2.example.ts.net&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>However, this isn’t reliable yet: When Tailscale’s startup takes a while during
system boot, the Node Exporter might burn through its entire restart budget when
it cannot listen on the Tailscale IP address yet. We can enable <a href="/posts/2024-01-17-systemd-indefinite-service-restarts/">indefinite
restarts</a> for the
service to eventually come up:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span><span style="color:#4070a0">&#34;prometheus-node-exporter&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    startLimitIntervalSec <span style="color:#666">=</span> <span style="color:#40a070">0</span>;
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      Restart <span style="color:#666">=</span> <span style="color:#4070a0">&#34;always&#34;</span>;
</span></span><span style="display:flex;"><span>      RestartSec <span style="color:#666">=</span> <span style="color:#40a070">1</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><h3 id="reliable-mount">N2. Reliable mounting</h3>
<p>While migrating my setup, I noticed that calling <a href="https://manpages.debian.org/mount.8"><code>mount(8)</code></a>
 from <code>unlock.service</code> directly is not reliable, and it’s better
to let systemd manage the mounting:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  fileSystems<span style="color:#666">.</span><span style="color:#4070a0">&#34;/srv&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/dev/mapper/data-data&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#666">=</span> <span style="color:#4070a0">&#34;ext4&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;nofail&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;x-systemd.requires=unlock.service&#34;</span>
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>Afterwards, I could just remove the <a href="https://manpages.debian.org/mount.8"><code>mount(8)</code></a>
 call
from <code>unlock.service</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">@@ -247,7 +247,10 @@ fry/U6A=
</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>         &#39;&#39;/bin/sh -c &#34;${pkgs.lvm2.bin}/bin/vgchange -ay&#34;&#39;&#39;
</span></span><span style="display:flex;"><span><span style="color:#a00000">-        &#39;&#39;/run/wrappers/bin/mount /dev/mapper/data-data /srv&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a00000"></span><span style="color:#00a000">+        # Let systemd mount /srv based on the fileSystems./srv
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+        # declaration to prevent race conditions: mount
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+        # might not succeed while the fsck is still in progress,
</span></span></span><span style="display:flex;"><span><span style="color:#00a000">+        # for example, which otherwise makes unlock.service fail.
</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span>       ];
</span></span><span style="display:flex;"><span>     };
</span></span></code></pre></div><p>In systemd services, I can now depend on the <code>/srv</code> mount unit:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>jellyfin <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    unitConfig<span style="color:#666">.</span>RequiresMountsFor <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/srv&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><h3 id="nginx-healthz">N3. nginx-healthz</h3>
<p>To save power, I turn off my NAS when they are not in use.</p>
<p>My backup orchestration uses Wake-on-LAN to start a wakeup and needs to wait
until the NAS is fully booted up and has mounted its <code>/srv</code> mount before it
can start backup jobs.</p>
<p>For this purpose, I have configured a web server (without any files) that
depends on the <code>/srv</code> mount. So, once the web server responds to HTTP requests,
we know <code>/srv</code> is mounted.</p>
<p>The <code>cloud-init</code> config looked as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">coreos</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">units</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>healthz.service<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">command</span>:<span style="color:#bbb"> </span>start<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        [Unit]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Description=nginx for /srv health check
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Wants=network.target
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        After=srv.mount
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Requires=srv.mount
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        StartLimitInterval=0
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        [Service]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        Restart=always
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/bin/sh -c &#39;systemctl is-active docker.service&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=/usr/bin/docker pull nginx:1
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=-/usr/bin/docker kill nginx-healthz
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStartPre=-/usr/bin/docker rm -f nginx-healthz
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">        ExecStart=/usr/bin/docker run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">            --name nginx-healthz \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">            --publish 10.0.0.252:8200:80 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">            --log-driver=journald \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">            nginx:1</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>The Docker version (ported from Flatcar Linux) looks like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>healthz <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;nginx for /srv health check&#34;</span>;
</span></span><span style="display:flex;"><span>    wants <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;network.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    unitConfig<span style="color:#666">.</span>RequiresMountsFor <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/srv&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>    startLimitIntervalSec <span style="color:#666">=</span> <span style="color:#40a070">0</span>;
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      Restart <span style="color:#666">=</span> <span style="color:#4070a0">&#34;always&#34;</span>;
</span></span><span style="display:flex;"><span>      ExecStartPre <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;/bin/sh -c &#39;systemctl is-active docker.service&#39; &#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker pull nginx:1&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker kill nginx-healthz&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker rm -f nginx-healthz&#39;&#39;</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      ExecStart <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            --name nginx-healthz \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            --publish 10.0.0.252:8200:80 \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            --log-driver=journald \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            nginx:1
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#39;&#39;</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>This configuration gets a lot simpler when migrating it from Docker to NixOS:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Signal readiness on HTTP port 8200 once /srv is mounted:</span>
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>firewall<span style="color:#666">.</span>allowedTCPPorts <span style="color:#666">=</span> [ <span style="color:#40a070">8200</span> ];
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>caddy <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    virtualHosts<span style="color:#666">.</span><span style="color:#4070a0">&#34;http://10.0.0.252:8200&#34;</span><span style="color:#666">.</span>extraConfig <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      respond &#34;ok&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>caddy <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    unitConfig<span style="color:#666">.</span>RequiresMountsFor <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/srv&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><h3 id="jellyfin">N4. NixOS Jellyfin</h3>
<p>The Docker version (ported from Flatcar Linux) looks like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  networking<span style="color:#666">.</span>firewall<span style="color:#666">.</span>allowedTCPPorts <span style="color:#666">=</span> [ <span style="color:#40a070">4414</span> <span style="color:#40a070">8096</span> ];
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>jellyfin <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;multi-user.target&#34;</span> ];
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;jellyfin&#34;</span>;
</span></span><span style="display:flex;"><span>    after <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;docker.service&#34;</span> <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>    requires <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;docker.service&#34;</span> <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>    startLimitIntervalSec <span style="color:#666">=</span> <span style="color:#40a070">0</span>;
</span></span><span style="display:flex;"><span>    serviceConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      Restart <span style="color:#666">=</span> <span style="color:#4070a0">&#34;always&#34;</span>;
</span></span><span style="display:flex;"><span>      ExecStartPre <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker pull lscr.io/linuxserver/jellyfin:latest&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker rm jellyfin&#39;&#39;</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>      ExecStart <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#39;&#39;-</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          --rm \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          --net=host \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          --name=jellyfin \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -e TZ=Europe/Zurich \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -v /srv/jellyfin/config:/config \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -v /srv/data/movies:/data/movies:ro \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -v /srv/data/series:/data/series:ro \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          -v /srv/data/mp3:/data/mp3:ro \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          lscr.io/linuxserver/jellyfin:latest
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#39;&#39;</span>
</span></span><span style="display:flex;"><span>      ];
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>As before, when using jellyfin from NixOS, the configuration gets simpler:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  services<span style="color:#666">.</span>jellyfin <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>jellyfin <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    unitConfig<span style="color:#666">.</span>RequiresMountsFor <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/srv&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><p>For a while, I had also set up compatibility symlinks that map the old location
(<code>/data/movies</code>, inside the Docker container) to the new location
(<code>/srv/data/movies</code>), but I encountered strange issues in Jellyfin and ended up
just re-initializing my whole Jellyfin state. While the required configuration
had more lines, I found it neat to move it into its own file, so here is how to
do that:</p>
<p>Remove the lines above from <code>configuration.nix</code> and move them into
<code>jellyfin.nix</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  config<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  lib<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  pkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  modulesPath<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>}:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>jellyfin <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    dataDir <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/srv/jellyfin&#34;</span>;
</span></span><span style="display:flex;"><span>    cacheDir <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/srv/jellyfin/config/cache&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>jellyfin <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    unitConfig<span style="color:#666">.</span>RequiresMountsFor <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/srv&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then, in <code>configuration.nix</code>, add <code>jellyfin.nix</code> to the <code>imports</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>   imports <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>     <span style="color:#235388">./hardware-configuration.nix</span>
</span></span><span style="display:flex;"><span>     <span style="color:#235388">./jellyfin.nix</span>
</span></span><span style="display:flex;"><span>   ];
</span></span></code></pre></div><h3 id="samba-nixos">N5. NixOS samba</h3>
<p>To use Samba from NixOS, I replaced my <code>systemd.services.samba</code> config from M3
with this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  services<span style="color:#666">.</span>samba <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    settings <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;global&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;map to guest&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;bad user&#34;</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;data&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;path&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/srv/data&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;comment&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;public data&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;read only&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;no&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;create mask&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;0775&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;directory mask&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;0775&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;guest ok&#34;</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  system<span style="color:#666">.</span>activationScripts<span style="color:#666">.</span>samba_user_create <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      smb_password=&#34;secret&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      echo -e &#34;$smb_password\n$smb_password\n&#34; | </span><span style="color:#70a0d0">${</span>lib<span style="color:#666">.</span>getExe&#39; pkgs<span style="color:#666">.</span>samba <span style="color:#4070a0">&#34;smbpasswd&#34;</span><span style="color:#70a0d0">}</span><span style="color:#4070a0"> -a -s michael
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#39;&#39;</span>;
</span></span></code></pre></div><p>Note: Setting the samba password in the activation script works for small
setups, but if you want to keep your samba passwords out of the Nix store,
you’ll need to use a different approach. On a different machine, I use
<a href="https://github.com/Mic92/sops-nix">sops-nix</a> to manage secrets and found that
refactoring the <code>smbpasswd</code> call like so works reliably:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>  setPasswords <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>writeShellScript <span style="color:#4070a0">&#34;samba-set-passwords&#34;</span> <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    set -euo pipefail
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    for user in michael; do
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        smb_password=&#34;$(cat /run/secrets/samba_passwords/$user)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        echo -e &#34;$smb_password\n$smb_password\n&#34; | </span><span style="color:#70a0d0">${</span>lib<span style="color:#666">.</span>getExe&#39; pkgs<span style="color:#666">.</span>samba <span style="color:#4070a0">&#34;smbpasswd&#34;</span><span style="color:#70a0d0">}</span><span style="color:#4070a0"> -a -s $user
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    done
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># …</span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>samba <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># …as above…</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span>samba-smbd<span style="color:#666">.</span>serviceConfig<span style="color:#666">.</span>ExecStartPre <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;</span><span style="color:#70a0d0">${</span>setPasswords<span style="color:#70a0d0">}</span><span style="color:#4070a0">&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  sops<span style="color:#666">.</span>secrets<span style="color:#666">.</span><span style="color:#4070a0">&#34;samba_passwords/michael&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    restartUnits <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;samba-smbd.service&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I also noticed that NixOS does not create a group for each user by default, but
I am used to managing my permissions like that. We can easily declare a group
like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  users<span style="color:#666">.</span>groups<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    gid <span style="color:#666">=</span> <span style="color:#40a070">1000</span>; <span style="color:#60a0b0;font-style:italic"># for consistency with storage3</span>
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    extraGroups <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;wheel&#34;</span> <span style="color:#60a0b0;font-style:italic"># Enable ‘sudo’ for the user.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;docker&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#60a0b0;font-style:italic"># By default, NixOS does not add users to their own group:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#60a0b0;font-style:italic"># https://github.com/NixOS/nixpkgs/issues/198296</span>
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;michael&#34;</span>
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><h3 id="rrsync-nixos">N6. NixOS rrsync</h3>
<p>The Docker version (ported from Flatcar Linux) looks like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>root<span style="color:#666">.</span>openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;&#39;command=&#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run --log-driver none -i -e SSH_ORIGINAL_COMMAND -v /srv/backup/midna:/srv/backup/midna stapelberg/docker-rsync /srv/backup/midna&#34; ssh-rsa AAAAB3Npublickey root@midna&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span></code></pre></div><p>To use <code>rrsync</code> from NixOS, I changed the configuration like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>root<span style="color:#666">.</span>openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;&#39;command=&#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>rrsync<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/rrsync /srv/backup/midna&#34; ssh-rsa AAAAB3Npublickey root@midna&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span></code></pre></div><h3 id="syncpl-nixos">N7. sync.pl script</h3>
<p>The Docker version (ported from Flatcar Linux) looks like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>root<span style="color:#666">.</span>openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;&#39;command=&#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>docker<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/docker run --log-driver none -i -e SSH_ORIGINAL_COMMAND -v /srv/data:/srv/data -v /root/.ssh:/root/.ssh:ro -v /etc/ssh:/etc/ssh:ro -v /etc/static/ssh:/etc/static/ssh:ro -v /nix/store:/nix/store:ro stapelberg/docker-sync&#34;,no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3Npublickey sync@dr&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span></code></pre></div><p>I wanted to stop managing the following <code>Dockerfile</code> to ship <code>sync.pl</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Dockerfile" data-lang="Dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span><span style="color:#4070a0">debian:stable</span><span style="">
</span></span></span><span style="display:flex;"><span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#60a0b0;font-style:italic"># Install full perl for Data::Dumper</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">RUN</span> apt-get update <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    <span style="color:#666">&amp;&amp;</span> apt-get install -y rsync ssh perl<span style="">
</span></span></span><span style="display:flex;"><span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ADD</span> sync.pl /usr/bin/<span style="">
</span></span></span><span style="display:flex;"><span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ENTRYPOINT</span> [<span style="color:#4070a0">&#34;/usr/bin/sync.pl&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>To get rid of the Docker container, I translated the <code>sync.pl</code> file into a Nix
expression that writes the <code>sync.pl</code> Perl script to the Nix store:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ pkgs }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># For string literal escaping rules (&#39;&#39;${), see:</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># https://nix.dev/manual/nix/2.26/language/string-literals#string-literals</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># For writers.writePerlBin, see https://wiki.nixos.org/wiki/Nix-writers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>pkgs<span style="color:#666">.</span>writers<span style="color:#666">.</span>writePerlBin <span style="color:#4070a0">&#34;syncpl&#34;</span> { libraries <span style="color:#666">=</span> []; } <span style="color:#4070a0">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0"># This script is run via ssh from dornröschen.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">use strict;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">use warnings;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">use Data::Dumper;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">if (my ($destination) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^([a-z0-9.]+)$/)) {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    print STDERR &#34;rsync version: &#34; . `</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>rsync<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/rsync --version` . &#34;\n\n&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    my @rsync = (
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;</span><span style="color:#70a0d0">${</span>pkgs<span style="color:#666">.</span>rsync<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/rsync&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;-e&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;ssh&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;--max-delete=-1&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;--verbose&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;--stats&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        # Intentionally not setting -X for my data sync,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        # where there are no full system backups; mostly media files.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;-ax&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;--ignore-existing&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;--omit-dir-times&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;/srv/data/&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;</span><span style="color:#4070a0;font-weight:bold">&#39;&#39;$</span><span style="color:#4070a0">{destination}:/&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    );
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    print STDERR &#34;running: &#34; . Dumper(\@rsync) . &#34;\n&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    exec @rsync;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">} else {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    print STDERR &#34;Could not parse SSH_ORIGINAL_COMMAND.\n&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">&#39;&#39;</span>
</span></span></code></pre></div><p>I can then reference this file by importing it in my <code>configuration.nix</code> and
pointing it to the <code>pkgs</code> expression of my NixOS configuration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ modulesPath<span style="color:#666">,</span> lib<span style="color:#666">,</span> pkgs<span style="color:#666">,</span> <span style="color:#666">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span><span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  syncpl <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> <span style="color:#235388">./syncpl.nix</span> { pkgs <span style="color:#666">=</span> pkgs; };
</span></span><span style="display:flex; background-color:#d8d8d8"><span><span style="color:#007020;font-weight:bold">in</span> {
</span></span><span style="display:flex;"><span>  imports <span style="color:#666">=</span> [ <span style="color:#235388">./hardware-configuration.nix</span> ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>root<span style="color:#666">.</span>openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#39;&#39;command=&#34;</span><span style="color:#70a0d0">${</span>syncpl<span style="color:#70a0d0">}</span><span style="color:#4070a0">/bin/syncpl&#34;,no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3Npublickey sync@dr&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># For interactive usage (when debugging):</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> [ syncpl ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># …</span>
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>This works, but is it the best approach? Here are some thoughts:</p>
<ul>
<li>By managing this script in a Nix expression, I can no longer use my editor’s
Perl support.
<ul>
<li>I could probably also keep <code>sync.pl</code> as a separate file and use string
interpolation in my Nix expression to inject an absolute path to the <code>rsync</code>
binary into the script.</li>
</ul>
</li>
<li>Another alternative would be to add a wrapper script to my Nix expression
which ensures that <code>$PATH</code> contains <code>rsync</code> and then the script wouldn’t need
an absolute path anymore.</li>
<li>For small glue scripts like this one, I consider it easier to manage the
contents “inline” in the Nix expression, because it means one fewer file in my
config directory.</li>
</ul>
<h3 id="flakes">N8. Sharing configs</h3>
<p>I want to configure all my NixOS systems such that my user settings are
identical everywhere.</p>
<p>To achieve that, I can extract parts of my <code>configuration.nix</code> into a
<a href="https://github.com/stapelberg/nix/blob/main/user-settings.nix"><code>user-settings.nix</code></a>
and then <a href="https://github.com/stapelberg/nix/blob/30cdd7db9e0ab4b7cc3a38b7953e1b7e1e238d75/flake.nix#L7">declare an accompanying
<code>flake.nix</code></a>
that provides this expression as an output.</p>
<p>After publishing these files in a git repository, I can reference said
repository in my <code>flake.nix</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    nixpkgs<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nixos/nixpkgs/nixos-25.05&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    stapelbergnix<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:stapelberg/nix&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      self<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      nixpkgs<span style="color:#666">,</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>	  stapelbergnix<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>    }:
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>      system <span style="color:#666">=</span> <span style="color:#4070a0">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      pkgs <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> nixpkgs {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>        config<span style="color:#666">.</span>allowUnfree <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      nixosConfigurations<span style="color:#666">.</span>storage2 <span style="color:#666">=</span> nixpkgs<span style="color:#666">.</span>lib<span style="color:#666">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> pkgs;
</span></span><span style="display:flex;"><span>        modules <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>          <span style="color:#235388">./configuration.nix</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>          stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>userSettings
</span></span><span style="display:flex; background-color:#d8d8d8"><span>          <span style="color:#60a0b0;font-style:italic"># Not on this machine; We have our own networking config:</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>          <span style="color:#60a0b0;font-style:italic"># stapelbergnix.lib.systemdNetwork</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>          <span style="color:#60a0b0;font-style:italic"># Use systemd-boot as bootloader</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>          stapelbergnix<span style="color:#666">.</span>lib<span style="color:#666">.</span>systemdBoot
</span></span><span style="display:flex;"><span>        ];
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      formatter<span style="color:#666">.</span><span style="color:#70a0d0">${</span>system<span style="color:#70a0d0">}</span> <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>nixfmt-tree;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>Everything <a href="https://github.com/stapelberg/nix/blob/main/user-settings.nix">declared in the
<code>user-settings.nix</code></a>
can now be removed from <code>configuration.nix</code>!</p>
<h3 id="immich-nixos">N9. Trying immich!</h3>
<p>One of the motivating reasons for switching away from CoreOS/Flatcar was that I
couldn’t try Immich, so let’s give it a shot on NixOS:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>  services<span style="color:#666">.</span>immich <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    host <span style="color:#666">=</span> <span style="color:#4070a0">&#34;10.0.0.252&#34;</span>;
</span></span><span style="display:flex;"><span>    port <span style="color:#666">=</span> <span style="color:#40a070">2283</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    mediaLocation <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/srv/immich&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Because /srv is a separate file system, we need to declare:</span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>services<span style="color:#666">.</span><span style="color:#4070a0">&#34;immich-server&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    unitConfig<span style="color:#666">.</span>RequiresMountsFor <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;/srv&#34;</span> ];
</span></span><span style="display:flex;"><span>    wantedBy <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;srv.mount&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>You can find the <a href="https://github.com/stapelberg/zkj-nas-tools/tree/master/_2025-07-nixos-nas-configs">full configuration directory on
GitHub</a>.</p>
<p>I am pretty happy with this NixOS setup! Previously (with CoreOS/Flatcar), I
could declaratively manage my base system, but had to manage tons of Docker
containers in addition. With NixOS, I can declaratively manage <em>everything</em> (or
as much as makes sense).</p>
<p>Custom configuration like my SSH+rsync-based backup infrastructure can be
expressed cleanly, in one place, and structured at the desired level of
abstraction/reuse.</p>
<p>If you’re considering managing at least one other system with NixOS, I would
recommend it! One of my follow-up projects is to convert storage3 (my other NAS
build) from Ubuntu Server to NixOS as well to cut down on manual
management. Being able to just copy the entire config to set up another system,
or try out an idea in a throwaway VM, is just such a nice workflow 🥰</p>
<p>…but if you have just a single system to manage, probably all of this is too
complicated.</p>
]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[How I like to install NixOS (declaratively)]]></title>
    <link href="https://michael.stapelberg.ch/posts/2025-06-01-nixos-installation-declarative/"/>
    <id>https://michael.stapelberg.ch/posts/2025-06-01-nixos-installation-declarative/</id>
    <published>2025-06-01T08:20:43+02:00</published>
    <content type="html"><![CDATA[<p>For one of my <a href="/posts/2023-10-25-my-all-flash-zfs-network-storage-build/#previously-coreos">network storage PC
builds</a>,
I was looking for an alternative to <a href="https://www.flatcar.org/">Flatcar Container
Linux</a> and tried out <a href="https://nixos.org/">NixOS</a> again
(after an almost 10 year break). There are many ways to install NixOS, and in
this article I will outline how I like to install NixOS on physical hardware or
virtual machines: over the network and fully declaratively.</p>
<h2 id="declarative">Introduction: Declarative?</h2>
<p>The term <a href="https://en.wikipedia.org/wiki/Declarative_programming">declarative</a>
means that you describe <em>what</em> should be accomplished, not how. For NixOS, that
means you declare what software you want your system to include (add to config
option
<a href="https://search.nixos.org/options?show=environment.systemPackages"><code>environment.systemPackages</code></a>,
or enable a module) instead of, say, running <code>apt install</code>.</p>
<p>A nice property of the declarative approach is that your system follows your
configuration, so by reverting a configuration change, you can cleanly revert
the change to the system as well.</p>
<p>I like to manage declarative configuration files under version control,
typically with Git.</p>
<p>When I originally set up my current network storage build, I chose CoreOS (later
Flatcar Container Linux) because it was an auto-updating base system with a
declarative <a href="https://cloud-init.io/">cloud-init</a> config.</p>
<h2 id="ways">Ways of installing NixOS</h2>
<h3 id="graphical-install">Graphical Installer: Only for Desktops</h3>
<p>The <a href="https://nixos.org/manual/nixos/stable/#ch-installation">NixOS manual’s “Installation”
section</a> describes a
graphical installer (“for desktop users”, based on the
<a href="https://en.wikipedia.org/wiki/Calamares_(software)">Calamares</a> system installer
and added in 2022) and a manual installer.</p>
<p>With the graphical installer, it’s easy to install NixOS to disk: just confirm
the defaults often enough and you’ll end up with a working system. But there are
some downsides:</p>
<ul>
<li>You need to manually enable SSH after the installation — locally, not via the
network.</li>
<li>The graphical installer generates an initial NixOS configuration for you, but
there is no way to inject your own initial NixOS configuration.</li>
</ul>
<p>The graphical installer is clearly not meant for remote installation or
automated installation.</p>
<h3 id="manual-install">Manual Installation</h3>
<p>The manual installer on the other hand is too manual for my taste: expand
“Example 2” and “Example 3” in <a href="https://nixos.org/manual/nixos/stable/#sec-installation-manual-summary">the NixOS manual’s Installation
summary</a>
section to get an impression. To be clear, the steps are very doable, but I
don’t want to install a system this way in a hurry. For one, manual procedures
are prone to mistakes under stress. And also, copy &amp; pasting commands
interactively is literally the opposite of writing declarative configuration
files.</p>
<h3 id="nixos-anywhere">Network Installation: nixos-anywhere</h3>
<p>Ideally, I would want to perform most of the installation from the comfort of my
own PC, meaning the installer must be usable over the network. Also, I want the
machine to come up with a working initial NixOS configuration immediately after
installation (no manual steps!).</p>
<p>Luckily, there is a (community-provided) solution:
<a href="https://github.com/nix-community/nixos-anywhere">nixos-anywhere</a>. You take care
of booting a NixOS installer, then run a single command and nixos-anywhere will
SSH into that installer, partition your disk(s) and install NixOS to
disk. Notably, nixos-anywhere is configured declaratively, so you can repeat
this step any time.</p>
<p>(I know that nixos-anywhere can even SSH into arbitrary systems and kexec-reboot
them into a NixOS installer, which is certainly a cool party trick, but I like
the approach of explicitly booting an installer better as it seems less risky
and more generally applicable/repeatable to me.)</p>
<h2 id="setup-nix">Setup: Installing Nix</h2>
<p>I want to use NixOS for one of my machines, but not (currently) on my main desktop PC.</p>
<p>Hence, I installed only the <code>nix</code> tool (for building, even without running
NixOS) on Arch Linux:</p>
<pre tabindex="0"><code>% sudo pacman -S nix
% sudo groupadd -r nixbld
% for n in $(seq 1 24); do sudo useradd -c &#34;Nix build user $n&#34; \
    -d /var/empty -g nixbld -G nixbld -M -N -r -s &#34;$(which nologin)&#34; \
    nixbld$n; done
% sudo systemctl enable --now nix-daemon.socket
</code></pre><p>Now, running <code>nix-shell -p hello</code> should drop you in a new shell in which the
GNU hello package is installed:</p>
<pre tabindex="0"><code>% export NIX_PATH=nixpkgs=channel:nixos-25.05
% nix-shell -p hello
hello

[nix-shell:/tmp]$ hello
Hello, world!
</code></pre><p>By the way, the <a href="https://wiki.archlinux.org/title/Nix">Nix page on the Arch Linux
wiki</a> explains how to use nix to install
packages, but that’s not what I am interested in: I only want to remotely manage
NixOS systems.</p>
<h2 id="own-installer">Building your own installer</h2>
<p>Previously, I said “you take care of booting a NixOS installer”, and that’s easy
enough: write the ISO image to a USB stick and boot your machine from it (or
select the ISO and boot your VM).</p>
<p>But before we can log in remotely via SSH, we need to manually set a password. I
also need to SSH with the <code>TERM=xterm</code> environment variable because the termcap
file of rxvt-unicode (my preferred terminal) is not included in the default
NixOS installer environment. Similarly, my configured locales do not work and my
preferred shell (Zsh) is not available.</p>
<p>Wouldn’t it be much nicer if the installer was pre-configured with a convenient
environment?</p>
<p>With other Linux distributions, like Debian, Fedora or Arch Linux, I wouldn’t
attempt to re-build an official installer ISO image. I’m sure their processes
and tooling work well, but I am also sure it’s one extra thing I would need to
learn, debug and maintain.</p>
<p>But building a NixOS installer is <em>very similar</em> to configuring a regular NixOS
system: same configuration, same build tool. The procedure is documented in the
<a href="https://wiki.nixos.org/wiki/Creating_a_NixOS_live_CD">official NixOS wiki</a>.</p>
<p>I copied the customizations I would typically put into <code>configuration.nix</code>,
imported the <code>installation-cd-minimal.nix</code> module from <code>nixpkgs</code> and put the
result in the <code>iso.nix</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#666">,</span> pkgs<span style="color:#666">,</span> <span style="color:#666">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  imports <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#235388">&lt;nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#235388">&lt;nixpkgs/nixos/modules/installer/cd-dvd/channel.nix&gt;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  i18n<span style="color:#666">.</span>supportedLocales <span style="color:#666">=</span> [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;en_DK.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;de_DE.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;de_CH.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    <span style="color:#4070a0">&#34;en_US.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  i18n<span style="color:#666">.</span>defaultLocale <span style="color:#666">=</span> <span style="color:#4070a0">&#34;en_US.UTF-8&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  security<span style="color:#666">.</span>sudo<span style="color:#666">.</span>wheelNeedsPassword <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      <span style="color:#4070a0">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5secret&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>      <span style="color:#4070a0">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5key&#34;</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    isNormalUser <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Michael Stapelberg&#34;</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    extraGroups <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;wheel&#34;</span> ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    initialPassword <span style="color:#666">=</span> <span style="color:#4070a0">&#34;SGZ3odMZIesxTuh2Y2pUaJA&#34;</span>;  <span style="color:#60a0b0;font-style:italic"># random for this post</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    shell <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>zsh;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    packages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  };
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    git  <span style="color:#60a0b0;font-style:italic"># for checking out github.com/stapelberg/configfiles</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    rsync
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    zsh
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    vim
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    emacs
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    wget
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    curl
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    rxvt-unicode  <span style="color:#60a0b0;font-style:italic"># for terminfo</span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>    lshw
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  ];
</span></span><span style="display:flex; background-color:#d8d8d8"><span>
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  programs<span style="color:#666">.</span>zsh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex; background-color:#d8d8d8"><span>  services<span style="color:#666">.</span>openssh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># This value determines the NixOS release from which the default</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># settings for stateful data, like file locations and database versions</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># on your system were taken. It‘s perfectly fine and recommended to leave</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># this value at the release version of the first install of this system.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Before changing this value read the documentation for this option</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).</span>
</span></span><span style="display:flex;"><span>  system<span style="color:#666">.</span>stateVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#34;25.05&#34;</span>; <span style="color:#60a0b0;font-style:italic"># Did you read the comment?</span>
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>To build the ISO image, I set the <code>NIX_PATH</code> environment variable to point <a href="https://manpages.debian.org/nix-build.1"><code>nix-build(1)</code></a>
 to the <code>iso.nix</code> file and to select the
upstream channel for NixOS 25.05:</p>
<pre tabindex="0"><code>% export NIX_PATH=nixos-config=$PWD/iso.nix:nixpkgs=channel:nixos-25.05
% nix-build &#39;&lt;nixpkgs/nixos&gt;&#39; -A config.system.build.isoImage
</code></pre><p>After about 1.5 minutes on my <a href="/posts/2025-05-15-my-2025-high-end-linux-pc/">2025 high-end Linux
PC</a>, the installer ISO can be
found in <code>result/iso/nixos-minimal-25.05.802216.55d1f923c480-x86_64-linux.iso</code>
(1.46 GB in size in my case).</p>
<h2 id="enabling-flakes">Enabling Nix Flakes</h2>
<p>Unfortunately, the nix project has not yet managed to enable the “experimental”
new command-line interface (CLI) by default yet, despite 5+ years of being
available, so we need to create a config file and enable the modern
<code>nix-command</code> interface:</p>
<pre tabindex="0"><code>% mkdir -p ~/.config/nix
% echo &#39;experimental-features = nix-command flakes&#39; &gt;&gt; ~/.config/nix/nix.conf
</code></pre><p>How can you tell old from new? The old commands are hyphenated (<code>nix-build</code>),
the new ones are separated by a blank space (<code>nix build</code>).</p>
<p>You’ll notice I also enabled <a href="https://wiki.nixos.org/wiki/Flakes">Nix flakes</a>,
which I use so that my nix builds are hermetic and pinned to a certain revision
of nixpkgs and any other nix modules I want to include in my build. I like to
compare flakes to version lock file in other programming environments: the idea
is that building the system in 5 months will yield the same result as it does
today.</p>
<p>To verify that flakes work, run <code>nix shell</code> (not <code>nix-shell</code>):</p>
<pre tabindex="0"><code>% nix shell nixpkgs#hello
/tmp 2 % hello
Hello, world!
</code></pre><h2 id="reinstall-steps">(Re-)Installation Steps</h2>
<p>For reference, here is the configuration I use to create a new VM for NixOS in
Proxmox. The most important setting is <code>bios=ovmf</code> (= UEFI boot, which is not
the default), so that I can use the same boot loader configuration on physical
machines as in VMs:</p>















<a href="https://michael.stapelberg.ch/posts/2025-06-01-nixos-installation-declarative/2025-05-17-proxmox-frigaten.jpg"><img
  srcset="https://michael.stapelberg.ch/posts/2025-06-01-nixos-installation-declarative/2025-05-17-proxmox-frigaten_hu_6bf4477cb712a89f.jpg 2x,https://michael.stapelberg.ch/posts/2025-06-01-nixos-installation-declarative/2025-05-17-proxmox-frigaten_hu_4c049a51d6ef646f.jpg 3x"
  src="https://michael.stapelberg.ch/posts/2025-06-01-nixos-installation-declarative/2025-05-17-proxmox-frigaten_hu_ad95b7d92750e4a1.jpg"
  alt="proxmox VM creation dialog screenshot" title="proxmox VM creation dialog screenshot"
  width="600"
  height="455"
  style="

border: 1px solid #000;

"
  
  loading="lazy"></a>



<p>Before we can boot our (unsigned) installer, we need to enter the UEFI setup and
disable Secure Boot. Note that Proxmox enables Secure Boot by default, for
example.</p>
<p>Then, boot the custom installer ISO on the target system, and ensure <code>ssh michael@nixos.lan</code> works without prompting for a password.</p>
<p>Declare a <code>flake.nix</code> with the following content:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    nixpkgs<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nixos/nixpkgs/nixos-25.05&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    disko<span style="color:#666">.</span>url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;github:nix-community/disko&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic"># Use the same version as nixpkgs</span>
</span></span><span style="display:flex;"><span>    disko<span style="color:#666">.</span>inputs<span style="color:#666">.</span>nixpkgs<span style="color:#666">.</span>follows <span style="color:#666">=</span> <span style="color:#4070a0">&#34;nixpkgs&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      nixpkgs<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      disko<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>      <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>    }:
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span>
</span></span><span style="display:flex;"><span>      system <span style="color:#666">=</span> <span style="color:#4070a0">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      pkgs <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">import</span> nixpkgs {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>        config<span style="color:#666">.</span>allowUnfree <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      nixosConfigurations<span style="color:#666">.</span>zammadn <span style="color:#666">=</span> nixpkgs<span style="color:#666">.</span>lib<span style="color:#666">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> system;
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">inherit</span> pkgs;
</span></span><span style="display:flex;"><span>        modules <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>          disko<span style="color:#666">.</span>nixosModules<span style="color:#666">.</span>disko
</span></span><span style="display:flex;"><span>          <span style="color:#235388">./configuration.nix</span>
</span></span><span style="display:flex;"><span>        ];
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      formatter<span style="color:#666">.</span><span style="color:#70a0d0">${</span>system<span style="color:#70a0d0">}</span> <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>nixfmt-tree;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Declare your disk config in <code>disk-config.nix</code>:</p>
<details>
<summary>disk-config.nix</summary>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ lib<span style="color:#666">,</span> <span style="color:#666">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  disko<span style="color:#666">.</span>devices <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    disk <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      main <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>        device <span style="color:#666">=</span> lib<span style="color:#666">.</span>mkDefault <span style="color:#4070a0">&#34;/dev/sda&#34;</span>;
</span></span><span style="display:flex;"><span>        type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;disk&#34;</span>;
</span></span><span style="display:flex;"><span>        content <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>          type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;gpt&#34;</span>;
</span></span><span style="display:flex;"><span>          partitions <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>            ESP <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>              type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;EF00&#34;</span>;
</span></span><span style="display:flex;"><span>              size <span style="color:#666">=</span> <span style="color:#4070a0">&#34;500M&#34;</span>;
</span></span><span style="display:flex;"><span>              content <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>                type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;filesystem&#34;</span>;
</span></span><span style="display:flex;"><span>                format <span style="color:#666">=</span> <span style="color:#4070a0">&#34;vfat&#34;</span>;
</span></span><span style="display:flex;"><span>                mountpoint <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/boot&#34;</span>;
</span></span><span style="display:flex;"><span>                mountOptions <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;umask=0077&#34;</span> ];
</span></span><span style="display:flex;"><span>              };
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>            root <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>              size <span style="color:#666">=</span> <span style="color:#4070a0">&#34;100%&#34;</span>;
</span></span><span style="display:flex;"><span>              content <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>                type <span style="color:#666">=</span> <span style="color:#4070a0">&#34;filesystem&#34;</span>;
</span></span><span style="display:flex;"><span>                format <span style="color:#666">=</span> <span style="color:#4070a0">&#34;ext4&#34;</span>;
</span></span><span style="display:flex;"><span>                mountpoint <span style="color:#666">=</span> <span style="color:#4070a0">&#34;/&#34;</span>;
</span></span><span style="display:flex;"><span>              };
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></details>
<p>Declare your desired NixOS config in <code>configuration.nix</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ modulesPath<span style="color:#666">,</span> lib<span style="color:#666">,</span> pkgs<span style="color:#666">,</span> <span style="color:#666">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  imports <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    [
</span></span><span style="display:flex;"><span>      (modulesPath <span style="color:#666">+</span> <span style="color:#4070a0">&#34;/installer/scan/not-detected.nix&#34;</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#235388">./hardware-configuration.nix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#235388">./disk-config.nix</span>
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Adding michael as trusted user means</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># we can upgrade the system via SSH (see Makefile).</span>
</span></span><span style="display:flex;"><span>  nix<span style="color:#666">.</span>settings<span style="color:#666">.</span>trusted-users <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;michael&#34;</span> <span style="color:#4070a0">&#34;root&#34;</span> ];
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Clean the Nix store every week.</span>
</span></span><span style="display:flex;"><span>  nix<span style="color:#666">.</span>gc <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    automatic <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    dates <span style="color:#666">=</span> <span style="color:#4070a0">&#34;weekly&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#666">=</span> <span style="color:#4070a0">&#34;--delete-older-than 7d&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#666">.</span>loader<span style="color:#666">.</span>systemd-boot <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    configurationLimit <span style="color:#666">=</span> <span style="color:#40a070">10</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  boot<span style="color:#666">.</span>loader<span style="color:#666">.</span>efi<span style="color:#666">.</span>canTouchEfiVariables <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>hostName <span style="color:#666">=</span> <span style="color:#4070a0">&#34;zammadn&#34;</span>;
</span></span><span style="display:flex;"><span>  time<span style="color:#666">.</span>timeZone <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Europe/Zurich&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Use systemd for networking</span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>resolved<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>  networking<span style="color:#666">.</span>useDHCP <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#666">.</span>network<span style="color:#666">.</span>networks<span style="color:#666">.</span><span style="color:#4070a0">&#34;10-e&#34;</span> <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    matchConfig<span style="color:#666">.</span>Name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;e*&#34;</span>;  <span style="color:#60a0b0;font-style:italic"># enp9s0 (10G) or enp8s0 (1G)</span>
</span></span><span style="display:flex;"><span>    networkConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>      IPv6AcceptRA <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>      DHCP <span style="color:#666">=</span> <span style="color:#4070a0">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  i18n<span style="color:#666">.</span>supportedLocales <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;en_DK.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;de_DE.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;de_CH.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;en_US.UTF-8/UTF-8&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  i18n<span style="color:#666">.</span>defaultLocale <span style="color:#666">=</span> <span style="color:#4070a0">&#34;en_US.UTF-8&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>mutableUsers <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>  security<span style="color:#666">.</span>sudo<span style="color:#666">.</span>wheelNeedsPassword <span style="color:#666">=</span> <span style="color:#60add5">false</span>;
</span></span><span style="display:flex;"><span>  users<span style="color:#666">.</span>users<span style="color:#666">.</span>michael <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    openssh<span style="color:#666">.</span>authorizedKeys<span style="color:#666">.</span>keys <span style="color:#666">=</span> [
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5secret&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5key&#34;</span>
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    isNormalUser <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>    description <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Michael Stapelberg&#34;</span>;
</span></span><span style="display:flex;"><span>    extraGroups <span style="color:#666">=</span> [ <span style="color:#4070a0">&#34;networkmanager&#34;</span> <span style="color:#4070a0">&#34;wheel&#34;</span> ];
</span></span><span style="display:flex;"><span>    initialPassword <span style="color:#666">=</span> <span style="color:#4070a0">&#34;install&#34;</span>;  <span style="color:#60a0b0;font-style:italic"># TODO: change!</span>
</span></span><span style="display:flex;"><span>    shell <span style="color:#666">=</span> pkgs<span style="color:#666">.</span>zsh;
</span></span><span style="display:flex;"><span>    packages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  environment<span style="color:#666">.</span>systemPackages <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">with</span> pkgs; [
</span></span><span style="display:flex;"><span>    git  <span style="color:#60a0b0;font-style:italic"># for checking out github.com/stapelberg/configfiles</span>
</span></span><span style="display:flex;"><span>    rsync
</span></span><span style="display:flex;"><span>    zsh
</span></span><span style="display:flex;"><span>    vim
</span></span><span style="display:flex;"><span>    emacs
</span></span><span style="display:flex;"><span>    wget
</span></span><span style="display:flex;"><span>    curl
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  programs<span style="color:#666">.</span>zsh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  services<span style="color:#666">.</span>openssh<span style="color:#666">.</span>enable <span style="color:#666">=</span> <span style="color:#60add5">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># This value determines the NixOS release from which the default</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># settings for stateful data, like file locations and database versions</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># on your system were taken. It‘s perfectly fine and recommended to leave</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># this value at the release version of the first install of this system.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># Before changing this value read the documentation for this option</span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic"># (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).</span>
</span></span><span style="display:flex;"><span>  system<span style="color:#666">.</span>stateVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#34;25.05&#34;</span>; <span style="color:#60a0b0;font-style:italic"># Did you read the comment?</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>…and lock it:</p>
<pre tabindex="0"><code>% nix flake lock
</code></pre><ol start="3">
<li>Using nixos-anywhere, fetch the hardware-configuration.nix from the installer and install NixOS to disk:</li>
</ol>
<pre tabindex="0"><code>% nix run github:nix-community/nixos-anywhere -- \
  --flake .#zammadn \
  --generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
  --target-host michael@nixos.lan
</code></pre><p>After about one minute, my VM was installed and rebooted!</p>
<aside class="admonition note">
  <div class="note-container">
    <div class="note-icon" style="width: 20px; height: 20px">
      <svg id="exclamation-icon" width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <path d="M0,0L24,0L24,24L0,24L0,0Z" style="fill:none;"/>
    <g transform="matrix(1.2,0,0,1.2,-2.4,-2.4)">
        <path d="M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM13,17L11,17L11,15L13,15L13,17ZM13,13L11,13L11,7L13,7L13,13Z" style="fill-rule:nonzero;"/>
    </g>
</svg>

    </div>
    <div class="admonition-content"><p><strong>Tip:</strong> Last month, I had to temporarily pin to the latest released version
(1.9.0) because of <a href="https://github.com/nix-community/nixos-anywhere/issues/510">issue
nixos-anywhere#510</a>
like so:</p>
<pre tabindex="0"><code>% nix run github:nix-community/nixos-anywhere/1.9.0 -- \
  […same as above…]
</code></pre></div>
  </div>
</aside>

<details>
<summary>Full <code>nixos-anywhere</code> installation transcript, if you’re curious</summary>
<pre tabindex="0"><code>% nix run github:nix-community/nixos-anywhere -- \      
  --flake .#wiki \                                                                 
  --generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
  --target-host michael@10.25.0.87                                               
Warning: Identity file /tmp/tmp.BT4E7i6eqJ/nixos-anywhere not accessible: No such file or directory.
### Uploading install SSH keys ###
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: &#34;/tmp/tmp.BT4E7i6eqJ/nixos-anywhere.pub&#34;
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.

Number of key(s) added: 1

Now try logging into the machine, with: &#34;ssh -i /tmp/tmp.BT4E7i6eqJ/nixos-anywhere -o &#39;IdentitiesOnly=no&#39; -o &#39;ConnectTimeout=10&#39; -o &#39;IdentitiesOnly=yes&#39; -o &#39;UserKnownHostsFile=/dev/null&#39; -o &#39;StrictHostKeyChecking=no&#39; &#39;michael@10.25.0.87&#39;&#34;
and check to make sure that only the key(s) you wanted were added.

### Gathering machine facts ###
Pseudo-terminal will not be allocated because stdin is not a terminal.
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
### Generating hardware-configuration.nix using nixos-generate-config ###
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
~/machines/wiki ~/machines/wiki
~/machines/wiki
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
warning: Git tree &#39;/home/michael/machines&#39; is dirty
warning: Git tree &#39;/home/michael/machines&#39; is dirty
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
Connection to 10.25.0.87 closed.
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
### Formatting hard drive with disko ###
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
umount: /mnt: not mounted
++ realpath /dev/sda
+ disk=/dev/sda
+ lsblk -a -f
NAME   FSTYPE   FSVER            LABEL                      UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
loop0  squashfs 4.0                                                                                    0   100% /nix/.ro-store
loop1                                                                                                           
loop2                                                                                                           
loop3                                                                                                           
loop4                                                                                                           
loop5                                                                                                           
loop6                                                                                                           
loop7                                                                                                           
sda                                                                                                             
├─sda1 vfat     FAT16                                       83DA-E750                                           
└─sda2 ext4     1.0                                         b136d6fd-d060-4b61-90fb-a8c1f9492f6e                
sr0    iso9660  Joliet Extension nixos-minimal-25.05-x86_64 1980-01-01-00-00-00-00                     0   100% /iso
+ lsblk --output-all --json
++ dirname /nix/store/fpwn44vygjj6bfn8s1jj9p8yh6jhfxni-disk-deactivate/disk-deactivate
+ bash -x
+ jq -r -f /nix/store/fpwn44vygjj6bfn8s1jj9p8yh6jhfxni-disk-deactivate/zfs-swap-deactivate.jq
+ lsblk --output-all --json
+ bash -x
++ dirname /nix/store/fpwn44vygjj6bfn8s1jj9p8yh6jhfxni-disk-deactivate/disk-deactivate
+ jq -r --arg disk_to_clear /dev/sda -f /nix/store/fpwn44vygjj6bfn8s1jj9p8yh6jhfxni-disk-deactivate/disk-deactivate.jq
+ set -fu
+ wipefs --all -f /dev/sda1
/dev/sda1: 8 bytes were erased at offset 0x00000036 (vfat): 46 41 54 31 36 20 20 20
/dev/sda1: 1 byte was erased at offset 0x00000000 (vfat): eb
/dev/sda1: 2 bytes were erased at offset 0x000001fe (vfat): 55 aa
+ wipefs --all -f /dev/sda2
/dev/sda2: 2 bytes were erased at offset 0x00000438 (ext4): 53 ef
++ type zdb
++ zdb -l /dev/sda
++ sed -nr &#39;s/ +name: &#39;\&#39;&#39;(.*)&#39;\&#39;&#39;/\1/p&#39;
+ zpool=
+ [[ -n &#39;&#39; ]]
+ unset zpool
++ lsblk /dev/sda -l -p -o type,name
++ awk &#39;match($1,&#34;raid.*&#34;) {print $2}&#39;
+ md_dev=
+ [[ -n &#39;&#39; ]]
+ wipefs --all -f /dev/sda
/dev/sda: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/sda: 8 bytes were erased at offset 0xc7ffffe00 (gpt): 45 46 49 20 50 41 52 54
/dev/sda: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
+ dd if=/dev/zero of=/dev/sda bs=440 count=1
1+0 records in
1+0 records out
440 bytes copied, 0.000306454 s, 1.4 MB/s
+ lsblk -a -f
NAME  FSTYPE   FSVER            LABEL                      UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
loop0 squashfs 4.0                                                                                    0   100% /nix/.ro-store
loop1                                                                                                          
loop2                                                                                                          
loop3                                                                                                          
loop4                                                                                                          
loop5                                                                                                          
loop6                                                                                                          
loop7                                                                                                          
sda                                                                                                            
sr0   iso9660  Joliet Extension nixos-minimal-25.05-x86_64 1980-01-01-00-00-00-00                     0   100% /iso
++ mktemp -d
+ disko_devices_dir=/tmp/tmp.YvWbz8ZKHk
+ trap &#39;rm -rf &#34;$disko_devices_dir&#34;&#39; EXIT
+ mkdir -p /tmp/tmp.YvWbz8ZKHk
+ destroy=1
+ device=/dev/sda
+ imageName=main
+ imageSize=2G
+ name=main
+ type=disk
+ device=/dev/sda
+ efiGptPartitionFirst=1
+ type=gpt
+ blkid /dev/sda
+ sgdisk --clear /dev/sda
Creating new GPT entries in memory.
The operation has completed successfully.
+ sgdisk --align-end --new=1:0:+500M --partition-guid=1:R --change-name=1:disk-main-ESP --typecode=1:EF00 /dev/sda
The operation has completed successfully.
+ partprobe /dev/sda
+ udevadm trigger --subsystem-match=block
+ udevadm settle --timeout 120
+ sgdisk --align-end --new=2:0:-0 --partition-guid=2:R --change-name=2:disk-main-root --typecode=2:8300 /dev/sda
The operation has completed successfully.
+ partprobe /dev/sda
+ udevadm trigger --subsystem-match=block
+ udevadm settle --timeout 120
+ device=/dev/disk/by-partlabel/disk-main-ESP
+ extraArgs=()
+ declare -a extraArgs
+ format=vfat
+ mountOptions=(&#39;umask=0077&#39;)
+ declare -a mountOptions
+ mountpoint=/boot
+ type=filesystem
+ blkid /dev/disk/by-partlabel/disk-main-ESP
+ grep -q TYPE=
+ mkfs.vfat /dev/disk/by-partlabel/disk-main-ESP
mkfs.fat 4.2 (2021-01-31)
+ device=/dev/disk/by-partlabel/disk-main-root
+ extraArgs=()
+ declare -a extraArgs
+ format=ext4
+ mountOptions=(&#39;defaults&#39;)
+ declare -a mountOptions
+ mountpoint=/
+ type=filesystem
+ blkid /dev/disk/by-partlabel/disk-main-root
+ grep -q TYPE=
+ mkfs.ext4 /dev/disk/by-partlabel/disk-main-root
mke2fs 1.47.2 (1-Jan-2025)
Discarding device blocks: done                            
Creating filesystem with 12978688 4k blocks and 3245872 inodes
Filesystem UUID: 57975635-9165-4895-93ea-72053294a185
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
	4096000, 7962624, 11239424

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (65536 blocks): done
Writing superblocks and filesystem accounting information: done   

+ set -efux
+ destroy=1
+ device=/dev/sda
+ imageName=main
+ imageSize=2G
+ name=main
+ type=disk
+ device=/dev/sda
+ efiGptPartitionFirst=1
+ type=gpt
+ destroy=1
+ device=/dev/sda
+ imageName=main
+ imageSize=2G
+ name=main
+ type=disk
+ device=/dev/sda
+ efiGptPartitionFirst=1
+ type=gpt
+ device=/dev/disk/by-partlabel/disk-main-root
+ extraArgs=()
+ declare -a extraArgs
+ format=ext4
+ mountOptions=(&#39;defaults&#39;)
+ declare -a mountOptions
+ mountpoint=/
+ type=filesystem
+ findmnt /dev/disk/by-partlabel/disk-main-root /mnt/
+ mount /dev/disk/by-partlabel/disk-main-root /mnt/ -t ext4 -o defaults -o X-mount.mkdir
+ destroy=1
+ device=/dev/sda
+ imageName=main
+ imageSize=2G
+ name=main
+ type=disk
+ device=/dev/sda
+ efiGptPartitionFirst=1
+ type=gpt
+ device=/dev/disk/by-partlabel/disk-main-ESP
+ extraArgs=()
+ declare -a extraArgs
+ format=vfat
+ mountOptions=(&#39;umask=0077&#39;)
+ declare -a mountOptions
+ mountpoint=/boot
+ type=filesystem
+ findmnt /dev/disk/by-partlabel/disk-main-ESP /mnt/boot
+ mount /dev/disk/by-partlabel/disk-main-ESP /mnt/boot -t vfat -o umask=0077 -o X-mount.mkdir
+ rm -rf /tmp/tmp.YvWbz8ZKHk
Connection to 10.25.0.87 closed.
### Uploading the system closure ###
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
copying path &#39;/nix/store/k64q0bbrf8kxvcx1zlvhphcshzqn2xg6-acl-2.3.2-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3rnsaxgfam1df8zx6lgcjbzrxhcg1ibg-acl-2.3.2-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ircpdw4nslfzmlpds59pn9qlak8gn81r-attr-2.5.2-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mrxc0jlwhw95lgzphd78s6w33whhkfql-attr-2.5.2-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qm7ybllh3nrg3sfllh7n2f6llrwbal58-bash-completion-2.16.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3frg3li12mwq7g4fpmgkjv43x5bqad7d-bash-interactive-5.2p37-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/88cs6k2j021mh2ir1dzsl6m8vqgydyiw-bash-interactive-5.2p37-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s3zz5nasd7qr894a8jrp6fy52pdrz2f1-bash-interactive-5.2p37-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/azy34jpyn6sskplqzpbcs6wgrajkkqy0-bind-9.20.9-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g28l15mbdbig59n102zd0ardsfisiw32-binfmt_nixos.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/k5r6p8gvf18l9dd9kq1r22ddf7ykfim2-build-vms.nix&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dxhfmzg1dhyag26r70xns91f8078vq82-alsa-firmware-1.2.4-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d46ilc6gzd1piyjfm9sbrl7pq3b3k0hg-busybox-1.36.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yq76x7ha0rv3mn9vxrar53zlkmxlkdas-bzip2-1.0.8-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9wvnmd2mr2qr8civvznnfi6s773fjvfh-coreutils-full-9.7-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/innps8d9bl9jikd3nsq8bd5irgrlay6f-curl-8.13.0-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6yiazrx84xj8m8xqal238g3mzglvwid2-dbus-1.14.10-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4bys54210khcipi91d6ivfz4g5qx33kh-dbus-1.14.10-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zh5iazbs69x4irfdml5fzbh9nm05spgb-dejavu-fonts-minimal-2.37&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/55wbmnssa48mi96pbaihz9wr4a44vxsd-diffutils-3.12-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qxk9122p34qwivq20k154jflwxjjjxb3-dns-root-data-2025-04-14&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/nqzrl9jhqs4cdxk6bpx54wfwi14x470f-e2fsprogs-1.47.2-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/b0qk1rsi8w675h1514l90p55iacswy5i-e2fsprogs-1.47.2-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/33ka30bacgl8nm7g7rcf2lz4n3hpa791-etc-bash_logout&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0sl4azq1vls6f7lfjpjgpn9gpmwxh3a5-etc-fuse.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m4xpifh68ayw6pn7imyiah5q8i03ibzx-etc-host.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qdhp1g45sqkz5limyh3pr54jr0vzrhyg-etc-lsb-release&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/61z4n7pkrbhhnahpvndvpc2iln06kcl3-etc-lvm-lvm.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l75amyv04p2ldiz6iv5cmlm03m417yfd-etc-man_db.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yb8n9alg0flvl93842savj8fk880a5s8-etc-modprobe.d-nixos.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v5zxfkpwma99vvbnwh7pv3qjvv09q9mf-etc-netgroup&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cb8dadanahyrgyh4yrd02j1pn4ipg3h1-etc-nscd.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ixzrf8qqzdp889kffwhi5l1i5b906wm2-etc-nsswitch.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mw3qf7jsf2cr6bdh2dwhsfaj46ddvdj4-etc-systemd-coredump.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ysxak9fplmg53wd40z86bacviss02wxj-etc-resolvconf.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w0027gbp2ppnzpakjqdsj04k1qnv8xai-etc-systemd-journald.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/73qr9mvgrkk9g351h1560rqblpv8bkli-etc-systemd-logind.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w8r9xylr9a1bd2glfp4zdwxiq8z2bhxb-etc-systemd-networkd.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/i1v3l8mmgr1zni58zsdgrf19xz5wpihs-etc-systemd-oomd.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/42mdjpbx4dablvbkj4l75xfjjlhpyb7a-etc-systemd-resolved.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g2d3zjbsa94jdqybcwbldzn3w98pwzhk-etc-systemd-sleep.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1wi887sd535dk4l4s0w7hp822fdys18j-etc-systemd-system-preset-00-nixos.preset&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/r4cjphi2kzkyvkc33y7ik3h8z1l5zs2q-etc-systemd-timesyncd.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n5y58mvq44mibwxkzzjb646v0nck9psd-etc-systemd-user-preset-00-nixos.preset&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7d2j36mn359g17s2qaxsb7fjd2bm4s7p-etc-systemd-user.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ziyrzq721iziyhvlchvg4zllcdr0rbd4-etc-zprofile&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6zl92vca58p27i20dck95j27lvj5lv16-etc-zinputrc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y7y1v7l88mxkljbijs7nwzm1gcg9yrjw-extra-utils&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kkbfwys01v37rxcrahc79mzw7bqqg1ha-X-Restart-Triggers-systemd-journald&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/15k9rkd7sqzwliiax8zqmbk9sxbliqmd-X-Restart-Triggers-systemd-journald-&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/08c95zkcyr5d4gcb2nzldf6a5l791zsl-fc-10-nixos-rendering.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fnrpg6pljxzbwz5f2wbiayirb4z63rid-fc-52-nixos-default-fonts.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hx4rm1z8sjh6s433sfxfjjwapr1r2lnm-X-Reload-Triggers-systemd-resolved&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/045cq354ckg28php9gf0267sa4qgywj9-X-Restart-Triggers-systemd-timesyncd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xj6dycqkvs35yla01gd2mmrrpw1d1606-fc-53-nixos-reject-type1.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c1l35xhz88v0hz3bfnzwi7k3pirk89gx-fc-53-no-bitmaps.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/izcym87m13m4nhjbxr2b2fp0r6wpl1s6-fontconfig-2.16.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/b5qqfs0s3fslirivph8niwdxh0r0qm4g-fc-cache&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yjab7vlimxzqpndjdqzann33i34x6pyy-findutils-4.10.0-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sgxf74s67kbx0kx38hqjzpjbrygcnl81-fuse-2.9.9-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3p531g8jpnfjl6y0f4033g3g2f14s32y-gawk-5.3.2-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vp5ra8m1sg9p3xgnz3zd7mi5mp0vdy25-fuse-3.16.2-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ndir5b1ag9pk4dyrpvhiidaqqg1xjdqm-gawk-5.3.2-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6hqzbvz50bm87hcj4qfn51gh7arxj8a6-gcc-14.2.1.20250322-libgcc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7dfxlvdhr5g57b1v8gxwpa2gs7i9g3y5-git-2.49.0-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ikhb97s6a22dn21lhxlzhambsmisrvff-gnugrep-3.11-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hgx3ai0sm533zfd9iqi5nz5vwc50sprm-fc-00-nixos-cache.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/i65zra2i21y5khnsnvl0pvd5rkvw5qhl-gnused-4.9-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/10p1z2bqsw0c6r5c5f59yn4lnl82lqxi-gnutar-1.35-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a9fcrsva5nw1y3nqdjfzva8cp4sj7l91-gzip-1.14-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0i7mzq93m8p7253bxnh7ydahmjsjrabk-gzip-1.14-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/diprg8qwrk8zwx73bjnjzjvaccdq5z1g-hicolor-icon-theme-0.18&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c7y25162xaplam12ysj17g5pwgs8vj99-hwdb.bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j4gc8fk7wazgn2hqnh0m8b12xx6m1n75-iana-etc-20250108&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dwv0wf3szv3ipgyyyrf1zxh4iqlckiip-inputrc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/nvz2hs89yjb8znxf7zw2y1rl8g0zc24g-intel2200BGFirmware-3.1-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8v0wnff8rpa64im6gkfwf702f0d13asb-iptables-1.8.11-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ib4za959rmvhyvhfn0p6y25szq9agzvv-X-Restart-Triggers-systemd-networkd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7vswj657kcfyz8g0i5lgm17k28nw9b6q-keymap&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1lcg48lg3yw873x21gybqzdmp06yqf0f-kmod-blacklist-31+20240202-2ubuntu8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m1arp7n5z5cqsv88l0gjazzfvkc8ia84-fontconfig-conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/q1f1r3hqs0h6gjkas71kzaafsnbipkp9-kmod-debian-aliases.conf-30+20230601-2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fanpm1fxx8x5wrizmddhqgqpxrw253bf-less-668-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qlbfg75i4wz6sb2ipzh4n1k0p8gp4wjp-lessconfig&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mhxn5kwnri3z9hdzi3x0980id65p0icn-lib.sh&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fsbyh73wsjl7gfl2k4rvdc6y02ixljmk-libcap-2.75-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5ja0hlyfnyvq1yyd2h8pzrmwwk9bgayy-libreelec-dvb-firmware-1.5.0-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mdf936r0ahj70lqqc09147msz4yxi3hb-libressl-4.0.0-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mddq2k6rmr77bz96j42y947wywcxin50-libcap-2.75-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yypqcvqhnv8y4zpicgxdigp3giq81gzb-libunistring-1.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ahfbv5byr6hiqfa2jl7pi4qh35ilvxzg-fontconfig-etc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6fmfvkxjq2q8hzvhmi5717i0zmwjkrpw-liburing-2.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/737acshv7jgp9jbg0cg9766m6izcwllh-link-units&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/303izw3zmxza3n01blxaa5a44abbqkkr-linux-6.12.30&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8pncaz101prqwhvcrdfx0pbmv4ayq5bf-linux-firmware-20250509-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hxrjrzngydk24ah8b5n8cl777n39y08b-linux-headers-6.12.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c4inn6fkfc4flai72ym5470jp2va8b6c-linux-pam-1.6.1-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x4a9ksmwqbhirjxn82cddvnhqlxfgw8l-linux-headers-static-6.12.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hi41wm3spb6awigpdvkp1sqyj0gj67vf-linux-pam-1.6.1-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m97qnhb417rmaiwwlw8qz2nvimgbmhxj-local-cmds&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6yd58721msbknn6fs57w0j82v04vpzw6-locale.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7kdkx4y7lbb15lb2qksw0nzal23mkhjy-login.defs&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x4mjvy4h92qy7gzi3anp0xbsw9icn3qj-logrotate.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/li71ly6mmsc7m9rm1hl98m4ka508s52i-lvm2-2.03.31-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1jj2lq1kzys105rqq5n1a2r4v59arz43-mailcap-2.1.54&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qkvqycyhqc9g9vpyp446b5cx7hv1c5zi-man-db-2.13.0-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gkbc6nv3h0hsp06kqk0p6s9911c2a1gg-mounts.sh&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qdv28rq2xlj68lsgrar938dq38v2lh5b-multiuser.nix&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6nkqdqzpa75514lhglgnjs5k4dklw4sb-libidn2-2.3.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vqykgcs16rs0ny39wlqb2hihb19f5bc8-nano-8.4-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6c69fcc0583xx7mqc4avszsv8dj1glfb-ncurses-6.5-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/smpby3mgssbggz941499y9x9r35w8cbh-nix-2.28.3-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/k9chrrif685hvkiqkc3fgfib19v2mh2y-nix-2.28.3-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bik2ny1bj83jby10lvq912i9v5gzy8g3-nix-bash-completions-0.6.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/90asb028hphm9iqh2h0xk3c52j3117rf-nix-zsh-completions-0.5.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lwhcdpa73h0p6z2hc8f5mqx6x03widq4-nixos-configuration-reference-manpage&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0249ff3p72ggrd308l2yk9n700f95kir-nixos-manual-html&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z8dgwwnab96n86v0fnr37mn107w26s1f-nixos-manual.desktop&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gj6hz9mj23v01yvq1nn5f655jrcky1qq-nixos-option.nix&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6fv8ayzjvgyl3rdhxp924zdhwvhz2iq6-nss-cacert-3.111&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l7rjijvn6vx8njaf95vviw5krn3i9nnx-nss-cacert-3.111-p11kit&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/as6v2kmhaz3syhilzzi25p9mn0zi9y0b-other.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d6kfv0rb15n92pi1jsjk65nd9264wja6-perl-5.40.0-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v0r2ndk31k1lsj967qrywdwxb87zdil6-perl5.40.0-Digest-HMAC-1.04&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l6b79dzj572yjifnwnrmjmf2r8qx1542-perl5.40.0-Encode-Locale-1.05&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mri94g6brszrzi5spdp3yjqig0dix246-perl5.40.0-FCGI-ProcManager-0.28&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vxmnihhgnkyd2yh1y6gsyrw7lzqyh0sn-perl5.40.0-File-Slurp-9999.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jvy29fslpki9ygmipnawxkacs0gdpwbg-perl5.40.0-HTML-TagCloud-0.38&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/187sf67ng5l08pirjv1hcnvvsx6bg6vi-perl5.40.0-Authen-SASL-2.1700&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/q6gp62h0h2z2lx3qh318crhikwc86m2y-perl5.40.0-HTML-Tagset-3.20&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1f3pkwqxmhglz59hdl9mizgaafrcxr2g-perl5.40.0-IO-HTML-1.004&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6insghd7kklnnilycdmbwl71l1gi9nkb-perl5.40.0-IO-Stringy-2.113&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cqa81jdkhwvkjnz810laxhd6faw8q917-perl5.40.0-JSON-4.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gvjb0301bm7lc20cbbp6q4mznb3k09j3-perl5.40.0-LWP-MediaTypes-6.04&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3fvhcxjgn3a4r6pkidwz9nd4cs84p6jv-perl5.40.0-Mozilla-CA-20230821&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pimqpkya3wybrpcm17zk298gpivhps5j-perl5.40.0-Test-Needs-0.002010&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wq3ij7g3r6jfkx61d3nbxrfmyw3f3bng-perl5.40.0-Test-RequiresInternet-0.05&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j5agsmr85pb3waxmzxn2m79yb1i7hhmh-perl5.40.0-TimeDate-2.33&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lyr4v74c0vw9j77fvr0d6dribm1lmfsr-perl5.40.0-Try-Tiny-0.31&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1hb2dxywm239rfwgdrd55z090hb1zbg3-perl5.40.0-URI-5.21&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5vc5pjg9yqxkxk855il2anp6jm5gkpa3-perl5.40.0-libnet-3.15&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kqz08h7qzxq13n3r3dymsl3jafgxl60x-php.ini&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/97qlbk0b8y0xs2hpjs37rp3sq6bdh99w-perl5.40.0-Config-IniFiles-3.000003&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/nfwlyasnxxdbnpiziw2nixwkz9b5f7g3-publicsuffix-list-0-unstable-2025-03-12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/30qhz45nwgfyns13ijq0nwrsjp8m7ypa-relaxedsandbox.nix&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5h63p4i2p25ba728pi4fr6vdcxa1227j-rt5677-firmware-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w5f538hq8zh4cxpjs3lx4jdhr2p6wvq8-rtl8192su-unstable-2016-10-05-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xnfzahna7b6jb6m1vdczap4v103qmr6w-perl5.40.0-Test-Fatal-0.017&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rb472zb1d7245j44iwm7xsnn9xkhv28r-rtl8761b-firmware-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/blgz4vzk56rbajaavr6kg437zr7jcabp-perl5.40.0-HTTP-Date-6.06&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3zf0hlfxwam2pcpr28374plf3zwcbkr0-perl5.40.0-Net-HTTP-6.23&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5i759vgj25fdy680l9v0sjhjg65q0q4h-perl5.40.0-WWW-RobotRules-6.02&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zqzjsf740jc5jqrzidw7qzkrsrl95d2b-rxvt-unicode-unwrapped-9.31-terminfo&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6602zq9jmd3r4772ajw866nkzn6gk1j0-sandbox.nix&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rg5rf512szdxmnj9qal3wfdnpfsx38qi-setup-etc.pl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kw5mdls5m8iqzh620iwm6h42rjqcbj93-shadow-4.17.4-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mvaibwlc8b5gfj13b3za7g5408hgjgwn-sof-firmware-2025.01.1-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x51649mj5ppmj97qrgxwr0calf82m9a5-perl5.40.0-File-Listing-6.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/krfhnl4n5a9w201z5pzwgps9fgz8z5j5-perl5.40.0-HTTP-CookieJar-0.014&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wcf11ld95pf7h1sn6nglgmrizbjlcw2f-sound-theme-freedesktop-0.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2g8wdl6qgkpk9dhj9rir7zkf9nxnjqzw-source&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ids7wg1swihwhh17qbdbpmbdx67k5w21-ssh-root-provision.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2959xcdddldhls7wslkm7gv2xf5pki1x-strace-6.14-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pilsssjjdxvdphlg2h19p0bfx5q0jzkn-strip.sh&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p7r0byvn43583rx7rvvy2pj44yv5c1jj-stub-ld-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v9ibkbvwc03ni062gh3ml4s0mswq0zfs-sudoers&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xx25wf50ww3bci4dvhfj2mrgccdfinja-system-generators&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/k5k5dvfz26a0py2xljmhz9a08y42gkkv-system-shutdown&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0iyxf2pmg0i16d4kxarqdfd3nqfa9mc5-systemd-257.5-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qyihkwbhd70ynz380whj3bsxk1d2lyc4-tzdata-2025b&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/q7pmljnijxmihsz0lsn90b7l2yvncvwm-udev-rules&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qzv2iqy6b9jl7x76pfcplqb81gs8sarx-unit--.slice&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5wap65qygkmwxnaykp2k00xbip0203ah-unit-dbus.socket&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/djhz08ld7cqvi36v4by31mr560lbbgdy-unit-fs.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yyjy3ni8amh8lmpgikv6qps1ygphhg9h-unit-fstrim.timer&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/68ymaa7yqz8b9c3m86awp9qrs3z5gmb9-unit-keys.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3mpivh2pqa1bbyp8h3n2wk8s0fvhp2rg-unit-local-fs.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3i90ba6lh4d8jd58kqgznxr53kzha657-unit-logrotate.timer&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/65pm1jd651q5891y7171sl2nsvnmh1a2-unit-multi-user.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m2chlkrf4dhjcnq50x6qnjlfvhz9c60s-unit-network-local-commands.service-disabled&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c1b80rjkrfis8704c9xxwl8chg0kpxd2-unit-nix-daemon.socket&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fl6il46drw769y6z9h4b89yv1k55xps3-unit-nixos-fake-graphical-session.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n4cwpsbmd30nhps87yic15rnxfvnlvaw-unit-phpfpm.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8zmflchf01g3wlj9j6csfnd47j0lgzcg-unit-post-resume.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/842zkhkx2aa0zy94qws3346dnd1cm3h6-unit-remote-fs.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9gkhxinv1884d1vy74rnkjd9vj2zn89p-unit-run-initramfs.mount&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4aiwrxc5i77s856dgx6b7yvqnxbq8x0g-unit-run-wrappers.mount&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gyxhzj5v8k01vwva1s476ny2zll2nvzm-unit-sysinit-reactivation.target&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p1k14mysynvbwyclk1nfjyyvcnrv65bp-unit-system-phpfpm.slice&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/10wi26kk0cjrifnvdsyrl8w4987z4hsb-unit-system.slice&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8g5vq29riss8693g7syg8n0bj2d7vc9l-unit-systemd-journald-audit.socket&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g29nsjbhdlc1xzgl0a0cybqvy9mg895l-unit-systemd-networkd.socket&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5wcg3gl5qzna3qn53id02sghbzfqa67z-unit-user-.slice&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7sb1nkpf82nb5kj7qc4bbqkwj1l1mdv9-update-users-groups.pl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l19w3rl6k8767i9znna0rfkjvl5cz4kg-urxvt-autocomplete-all-the-things-1.6.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ym3cf4rnxblhlpsxj2cd5wm8rp8pgfr7-urxvt-perls-2.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/na57lsanf2c453zdz1508wnzvbh9w4rg-urxvt-resize-font-2019-10-05&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l8qxbnizariir6sncianl8q0i4a0zaya-urxvt-tabbedex-19.21&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d8k53n8mmb8j1a6v4f3wvhhap8xwcssd-urxvt-theme-switch-unstable-2014-12-21&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3fgvp4zddvbkkyviq5sajbl7wc7lmx5q-user-generators&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/k7bynf83k39pk9x6012vjrd6fll2wdqh-useradd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wcrrwx3yvbvwa1hryjpgcbysdf8glnix-util-linux-2.41-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vq0m8mcigxkjfjdwrgzvizjam5vx669h-wireless-regdb-2025.02.20-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rqxaqpliqlygv3hw53j4j7s54qj5hjri-vconsole.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/za53jjhjl1xajv3y1zpjvr9mh4w0c1ay-xgcc-14.2.1.20250322-libgcc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/18w6fpxmn5px02bpfgk702bs9k7yj5ml-xorgproto-2024.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l63r9kidyd8siydvr485g71fsql8s48b-xz-5.8.1-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3drnnkrsdfrqdrdg425wda83k79nlmwp-xz-5.8.1-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lxbkfad3nbyfx3hsc1ajlvqs3s67li6x-zd1211-firmware-1.5-zstd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wysmwjrvwx7gk5w6dxd0d1jwjbjj350a-zsh-5.9-doc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4y2v97rjk4mic266vzbvmlxjnjnisnmm-zsh-5.9-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/af0jrnzsydq1i28vcnkpgp0110ac2cj3-zsh-5.9-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/184bcjcc97x3klsz63fy29ghznrzkipg-zstd-1.5.7-man&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cg9s562sa33k78m63njfn1rw47dp9z0i-glibc-2.40-66&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8syylmkvnn7lg2nar9fddpp5izb4gh56-attr-2.5.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a6w0pard602b6j7508j5m95l8ji0qvn6-aws-c-common-0.10.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xy4jjgw87sbgwylm5kn047d9gkbhsr9x-bash-5.2p37&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7a8gf62bfl22k4gy2cd300h7cvqmn9yl-brotli-1.1.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6ycmjimp1h3z4xgf47jjxxmps9skbdw1-cpio-2.15&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w762xfdg6qkyamncs8s33m182n45nmma-dav1d-1.5.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pyfpxwjw1a7fj5j7n2czlk4g7lvzhvhy-dosfstools-4.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2x51wvk10m9l014lyrfdskc3b360ifjp-ed-1.21.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p9k7bd23v5yvmap9594f9x7hpvacdh32-expand-response-params&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j0bzxly2rvcym1zkhn393adiqcwn8np6-expat-2.7.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/719j8zd8g3pa5605b7a6w5csln323b1x-fribidi-1.0.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qlwqqqjdvska6nyjn91l9gkxjjw80a97-editline-1.17.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zrnqzhcvlpiycqbswl0w172y4bpn0lb4-bzip2-1.0.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fcyn0dqszgfysiasdmkv1jh3syncajay-gawk-5.3.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7c0v0kbrrdc2cqgisi78jdqxn73n3401-gcc-14.2.1.20250322-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qkzkz12l4q06lzbji0ifgynzrd44bpjs-gdbm-1.25-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d9x4blp2xwsbamz8az3c54x7la08j6ln-giflib-5.2.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1abbyfv3bpxalfjfgpmwg8jcy931bf76-bzip2-1.0.8-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/303islqk386z1w2g1ngvxnkl4glfpgrs-glibc-2.40-66-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3mi59bgj22xx29dyss7jhmx3sgznd85m-acl-2.3.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zhpgx7kcf8ii2awhk1lz6p565vv27jv5-attr-2.5.2-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w4hr24l1bfj07b56vm3zrp0rzxsd3537-aws-c-compression-0.3.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ifvslnvmvg3nb26yliprya6ja1kb5yaf-aws-c-sdkutils-0.2.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/26ddah1lva210rn57dzkan1dgjvj7dn4-aws-checksums-0.2.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/if83fp73ln7ksdnp1wkywvyv53b6fw3f-glibc-2.40-66-getent&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dwwc14ppzkl0yphcgsz25xvi24c9d1zm-gmp-6.3.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c341wfmk7r827k691yp5ynjnv5014xqf-audit-disable&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rjlwg1dlbhkv2bhrq03m794xbhcwcgh6-audit-stop&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1191qk37q1bxyj43j0y1l534jvsckyma-acl-2.3.2-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/padpqlhkvnr56a5j4ma5mlfrp46ibg7g-container-init&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y7g9g1gfg1f6y3gm2h02i7hmjzv10f9q-dav1d-1.5.1-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/24gnm4vyck53sppsvlzcmknvz7jp8x0p-firewall-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y7ljc4ir2hkwkr7lhgm9xj5hw3kw8275-firewall-stop&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cab2yvnph1hfym998vdq0q4nr9zfndrs-gnum4-1.4.19&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j2v7jjnczkj7ra7jsgq6kv3242a1l52x-getent-glibc-2.40-66&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/clbb2cvigynr235ab5zgi18dyavznlk2-gnused-4.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wrxvqj822kz8746608lgns7h8mkpn79f-gnutar-1.35&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pl3wb7v54542kdaj79dms8r2caqbn0nv-gpm-unstable-2020-06-17&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/afhkqb5a94zlwjxigsnwsfwkf38h21dk-gzip-1.14&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/677sx4qrmnmgk83ynn0sw8hqgh439g6b-json-c-0.18&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4v64wga9rk0c919ip673j36g6ikx26ha-keyutils-1.6.3-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bkm4ppw3rpyndsvy5r18fjpngg2730ip-libICE-1.1.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/psjc7gv2314bxncywpvsg76gvbk2dn00-libXau-1.0.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/aq5b44b37zp5dfwz5330pxqm699gs4g3-isl-0.20&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hx0kbryivbs7qccnvpmr17y6x818dhxc-libXdmcp-1.1.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mhhia7plis47fhrv713fmjibqal96w1g-libaio-0.3.113&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1rlljm73ch98b2q9qqk8g0vhv2n9mya8-libapparmor-4.1.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qsyxh2zqqkqzaaa0v5scpjz364ksmj3m-libargon2-20190702&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/r25srliigrrv5q3n7y8ms6z10spvjcd9-glibc-2.40-66-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wcjq2bl1vhvnc07xzl5m41jncf745yz4-firewall-reload&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bh1hxs692a2fv806wkiprig10j5znd7c-libcap-2.75-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/142lbjxi74mv9nkb9k4831v2x8v5w5zv-bison-3.8.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/nsi5mszs52rj3hgkpa8cnc90nnqvl11a-boehm-gc-8.2.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z98iwn19jjspfha4adjkp32r5nj56grw-bootspec-1.0.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x9hwyp3ld0mdqs8jcghshihwjdxm114l-boehm-gc-8.2.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fm2ky0fkkkici6zpf2s41c1lvkcpfbm5-db-4.8.30&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/10glq3a1jbsxv50yvcw1kxxz06vq856w-db-5.3.28&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wzwlizg15dwh6x0h3ckjmibdblfkfdzf-flex-2.6.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sdqvwr8gc74ms9cgf56yvy409xvl8hsf-gettext-0.22.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kxhsmlrscry4pvbpwkbbbxsksmzg0gp0-gmp-with-cxx-6.3.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/nzg6zqsijbv7yc95wlfcdswx6bg69srq-gmp-with-cxx-6.3.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/088li1j480s9yv1736wiz7a26bxi405w-graphite2-1.3.14&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s86p50hcjcp9phyv9gxd5hra8nwczvrk-groff-1.23.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x4b392vjjza0kz7wxbhpji3fi8v9hr86-gtest-1.16.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rw826fx75sw7jywfvay6z5a6cnj74l1g-icu4c-73.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9hpylx077slqmzb5pz8818mxjws3appp-iputils-20240905&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y4ygj0jgwmz5y8n7jg4cxgxv4lc1pwfy-jemalloc-5.3.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jgxvk139zdfxi1wgdi9pjj1yhhgwvrff-lerc-4.0.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ckwwqi6p7x3w64qdhx14avy2vf8a4wiq-libICE-1.1.2-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x2wlg9cm3yrinz290r4v2fxpbpkw8gki-libcap-2.75&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2bjcjfzxnwk3zjhkrxi3m762p8dv6f1s-libcap-ng-0.8.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/87fck6hm17chxjq7badb11mq036zbyv9-coreutils-9.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dfznrcrr2raj9x4bdysvs896jfnx84ih-libcbor-0.12.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jrd3xs0yvb2xssfqn38rfxhnzxz9827s-libcpuid-0.7.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w53vh0qqs6l2xm4saglkxaj97gi50nr5-libdatrie-2019-12-20-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/amy9kqbm05wv18z5z66a3kprc2ccp390-libdeflate-1.23&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yai7mpy5d4rw0jvflyxdf0vzjkiqxhv6-libevent-2.1.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/90c412b9wqhfny300rg5s2gpsbrqb31q-libffi-3.4.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9z7wv6k9i38k83xpbgqcapaxhdkbaqhz-libgpg-error-1.51&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vwj8664lvyx3svjp856baijyk17vv9lc-libidn-1.42&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9f6bvnw1hxy79shw6lva854ck3cmi43j-libjpeg-turbo-3.0.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/56fi3kcbg9haxf5c1innrn2p9dx2da2j-libmd-1.1.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9hbdbr5hikxjb16ir40w2v24gbivv22x-libmnl-1.0.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ygz5dcpzd7qkw44wpbd65rl6amwpxp5f-libnfnetlink-1.0.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/635dz3p1afjwym9snp2r9hm0vaznwngy-libnl-3.11.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/59j7x0s1zybrjhnq5cv1ksm0di4zyb4n-libpipeline-1.5.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2sbq4hd9imczmbb5za1awq0gvg0cbrwr-libbsd-0.12.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bxs5j3zhh35nwhyhwc3db724c7nzfl36-libpsl-0.21.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/q0dsazc8234b7imr9y4vv5rv09r58mqi-libptytty-2.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/f7y5q4jwja2z3i5zlylgbv5av6839a54-libnftnl-1.2.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6wrjb93m2arv7adx6k2x9nlb0y7rmgpi-libnetfilter_conntrack-1.1.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kvycshxci0x434bcgnsvr9c0qgmsw6v5-libressl-4.0.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a7zbljj0cwkbfzn22v6s2cbh39dj9hip-libseccomp-2.6.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7h0sard22wnbz0jyz07w8y9y0fcs795r-diffutils-3.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1wp5qqj9n3ccjvlbhpdlg9pp9dpc00ns-copy-extra-files&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7y59hzi3svdj1xjddjn2k7km96pifcyl-findutils-4.10.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rmrbzp98xrk54pdlm7cxhayj4344zw6h-libassuan-3.0.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0dqmgjr0jsc2s75sbgdvkk7d08zx5g61-libgcrypt-1.10.3-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9gzvhlrpxmkhggn32q7q9r38cfg6gasn-libsodium-1.0.20&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zf61wng66ik05clni78571wfmfp5kqzq-libtasn1-4.20.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z53ai32niqhghbqschnlvii5pmgg2gcx-libthai-0.1.29&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/np37flx1k0dj0j0xgxzkxs069sb5h4k3-libtool-2.5.4-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1warn5bb3r7jwfkpdgr4npab3s63sivj-liburcu-0.15.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9mcjnb75xq17mvr8ikm3sg5yhx6ga62r-libuv-1.50.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sh1rrkag3x04p0gs80723iwfdwlysxf8-libvmaf-3.0.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qn01pv62sbpzbsy0a6m0q23syrmkk3bv-libxcb-1.17.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qizipyz9y17nr4w4gmxvwd3x4k0bp2rh-libxcrypt-4.4.38&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1r4qwdkxwc1r3n0bij0sq9q4nvfraw6i-libpcap-1.10.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xv0pc5nc41v5vi0lac1i2d353s3rqlkm-libxml2-2.13.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/39zbg3zrp77ima6ih51ihzlzmm1yj5vh-libyuv-1908&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3mqzj6ndzyy2v86xm70d5hdd1nsl1y9f-lm-sensors-3.6.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g3j7jsv3nsfnxkq98asi01n0fink0dk9-llhttp-9.2.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/iyh7nfcs7f249fzrbavqgxzwiy0z7xii-lowdown-1.3.2-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/51sr6m5fb8fff9vydnz7gkqyl5sjpixl-lz4-1.10.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gpbn3j498s0909h5j8fb3h4is8dn8rll-lzo-2.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zfb1cj0swnadhvfjvp0jm2zhgwiy927f-make-initrd-ng-0.1.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/h4zr885cac368xv73qrhscbpc7irqly8-mcpp-2.7.2.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g2gsgbka17hdr999v8k9yhkq825mb6zz-mkpasswd-5.6.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mpvxc1dbpnk74345lk69dw497iqcjvj0-libX11-1.8.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9nn8vbf2n55zkb7dh6ldxckbis3pkh30-libaom-3.11.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3ccwi70k69wrxq6nxy6v3iwwvawgsw6m-libressl-4.0.0-nc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/029cprg174i7c4gvn1lwmnm4vdl6k8df-libvmaf-3.0.0-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wxkbp7kwvpxvjh28rigmf6lfq64zlsyj-iptables-1.8.11&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2l8jg5lpi7084sc1q33jmpd7fph41n2g-libxcb-1.17.0-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wyf93cvh25b2xg82mkjcpmwgcspk0ggr-mpdecimal-4.0.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n52k1dccv0mipz1s4gkk45x64cmmcvrf-mpfr-4.2.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/iszvcck61smiji8gxmbf02z3gi8zr7i3-mtools-4.0.48&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/74is8yi7sy8q58xg806fy0ja99fswjva-libxslt-1.1.43&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mhmg8c5dmx8qi63rlz347931br8bmq08-ncompress-5.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vfmnmqsnfiiqmphy7ffh2zqynsxfck1q-ncurses-6.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/skd9hg5cdz7jwpq1wp38fvzab9y8p0m6-net-tools-2.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m4yrdwg3zv50mw8hy2zni5dyy7ljlg7j-nettle-3.10.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v7rzgm8p6p0ghg5mqcin4vbx6pcrvc0j-nghttp2-1.65.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hbjkfqhx0anx8x2rs0d9kbfhy80jfc7n-nixos-build-vms&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/833wqy1r0qpp5h5vd4yiqm5f2rjjc7jg-node_exporter-1.9.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ci5nyvrii461hnaw267c1zvna0sjfxif-npth-1.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6czlz4s2n2lsvn6xqlfw59swc0z21n89-nsncd-1.5.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/82n465240j5a8ap7c60gqy3a6kwqv1rs-numactl-2.0.18&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z8rlklqfzxq7azbzyp30938x7wh5zf3c-oniguruma-6.9.10-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mb407pssv7zc7pfb4d910k6fshfagm6j-libmpc-1.3.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zllk6n33p6mx8y9cf4vhs2brcbis3na4-libX11-1.8.12-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gmirqf6vp6rskn2dhfyd7haphy6kjnvk-libXext-1.3.6&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/21aj13sj7jg5ld96s3q7nd40s1iwzfld-libXfixes-6.0.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a5mrmf5bjmjfq2y90hsn8xnw3lb0cqil-libXpm-3.5.17&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/md8kapandyhs7bbw5s782aanw38p2kax-gnupg-2.4.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pbg3xkihyscyx3978z0pfc0xixb10pf6-libXrender-0.9.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9m6a4iv2nh6v4aga830r499s4arknsfb-p11-kit-0.25.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8pviily4fgsl02ijm65binz236717wfs-openssl-3.4.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4sfqx63v2k8argz2wmnbspr0rh49y1c1-libXi-1.8.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x0kaspzb5jqvgp357bj27z6iq24ximfg-patch-2.7.6&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ifbr2frwmyf8p0a260hn5vzg3cagww14-pcre-8.45&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a9c6rz5183psp30q1nhkakis6ab4km4b-pcre2-10.44&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pkxrqwd26nqr7gh9d4gi9wf7hj6rk29a-libXcursor-1.2.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sdqzcyfy52y0vf10nfsxy3mhv9b2vmkv-jq-1.7.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wlzslync0dv270mvi1f7c0s1hf4p27yf-pcre2-10.44&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qqpgwzhpakcqaz6fiy95x19iydj471ca-pcsclite-2.3.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pi7vpdqikh160rj4vyfh58x0z2hksgj7-libaom-3.11.0-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/r4bvdpg1761bqc4jxn4sqxr6ymbcdw8f-perl5.40.0-Clone-0.46&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vqhxms7i64vb86p07i8q50x32yi9gv5c-perl5.40.0-FCGI-0.82&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bafwfabi148bigqra4nc5w029zj7dx7c-perl5.40.0-TermReadKey-2.38&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/clh9w1vpiijlv9a1i6gjkvwziwqzsp78-php-calendar-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bvh4vwr9dr5iaiz57igi5b4mryqnwpaa-php-bcmath-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vim07ywfgdqz217qnmik9knbmm5glpcn-perl5.40.0-HTTP-Message-6.45&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n9ch6ggimi6ri5vx62mqmzgrrkb3qfwg-jq-1.7.1-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gwdxl7y6c54smp9x268diyjqwg1naylk-php-ctype-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gqmr3gixlddz3667ba1iyqck3c0dkpvd-gnugrep-3.11&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ybd0aamz6dwc51x1ab62b7qycccqb0z0-libselinux-3.8.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/b78nah09ykpmxky3br6fl5akjjwcg1g5-php-dom-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mq46ybxbw3f7jcv07hlk06sa8cqwy4f8-php-exif-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wfsrpgcf3mpl9np032nhj6rvz53y4la5-php-fileinfo-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1f91m6wkjrm4v6317za4wklgqh6qraan-php-filter-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j6rlrmd7jqk1902s98mjjxj8d563hv8q-perl5.40.0-HTML-Parser-3.81&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cxqg93vhjddswn75f5bdzbkcpbix32gg-perl5.40.0-HTTP-Cookies-6.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dfyds9allpyy0nwhr2j729jvkb49mrxn-perl5.40.0-HTTP-Daemon-6.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z88zb5wamza6irc3lkz6aj52ga3q5sl3-libaom-3.11.0-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3pd9kl0nnn22in35ad4p6v5zha8s24gj-perl5.40.0-HTTP-Negotiate-6.01&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9mlfyarh1nzzzc0zsn6vf0axdjjbq2l4-gpm-unstable-2020-06-17&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/66ld17ifbjz63firjjv88aydxsc3rcs6-less-668&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xs1qm9vidbfn1932z9csmnwdkrx4lch6-libedit-20240808-3.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/i9b4ix0ih1qnf2b4rj0sxjzkqzqhg7mk-php-ftp-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bssap9z2zp2nbzzr6696dqqr6axac57g-php-gettext-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fmf4095h2x5rxsgsyxz655049k2ibchl-perl5.40.0-CGI-4.59&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pdknwq3rbhy1g6343m4v45y98zilv929-php-gmp-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/f3fc31rc8gnmbbz0naniaay6406y5xy8-php-iconv-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sxi98visi8s3yk1p05ly3pljh683wg1f-php-intl-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/h57pwp22kkwjm3yqh3id3ms2jymc27rq-php-mbstring-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jlzg258kgf0k3vcn54p91n43kb8afllk-php-mysqli-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j0ljd9127519pkb01zbyxcf42kjhp2l8-aws-c-cal-0.8.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/axi2kqwlrr7lvkfj42p7mav2x7apffrq-coreutils-full-9.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bmjb20jhxkq881f43pd264240sp677an-krb5-1.21.3-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4qks83jh0avrs4111c6rlwn3llqlily0-ldns-1.8.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bmckdjhp1cn78n4md1m55zglpqxwijj3-libtpms-0.10.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d4knbwl8kbkaai01bp5pb0ph0xpb7bnz-perl5.40.0-Net-SSLeay-1.92&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/idgpi0g62yyq8plhrdc2ps2gcrkd44jz-dash-0.5.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fipal54rj1drz2q567pacy6q2gsnm2hq-php-opcache-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/57csaam2bhhfzbhw2j70ylaxq25wj09g-php-openssl-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lapy63xgm8gpjbxj55g4w74idmbnavzm-php-pcntl-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n1lb8wdk0avd6f06fhiydwa2f4x91pz4-php-pdo-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/47kv3znq3rx6lhp5h3shs2zx0gd7r3zv-php-pdo_mysql-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0gn4mrgjlx9dhffs19yshpdrhi9pcbyl-perl5.40.0-CGI-Fast-2.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yqj36zvdh3nmv5fpyfsp5mr06h1n4npc-php-posix-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7yi71ajqcpsdmz1qa6r8aprm6vgqj74s-php-session-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fmj7d945ychgybv2bld5dnk4lzcm1m10-php-simplexml-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w8cc84nvjzikcrgfc0pi8qap5wiq1cb8-php-soap-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9rz570nz0d51y8r6dmjniqqbzgc4bnrg-php-sockets-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/svl7fda13ygkdyvisywihlsslrcqvbp8-php-sodium-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/70aw279ymnhp8dadzwfk4clh4f1m7wsn-php-sysvsem-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3ym3wzyd6z19hfqzqwchzqbd9vzdk345-php-tokenizer-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j04vms72z22650kvbx69b8qkpbgi5na6-php-xmlreader-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n75k5rjvbc7gfp4zpajpab5rykajdcmy-perl5.40.0-IO-Socket-SSL-2.083&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1c0xn8mx6ha6fcpaxi4p0q16lvr7zfrr-php-xmlwriter-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/r7sp55wajh5p7yh2ahgifr1c8jbqjgnl-pixman-0.44.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dli16nly2z52s1mi1phbcgmhw7nkq7x6-pkg-config-0.29.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6mnmfhfsz94zgsyskz7zanian98ssykf-bind-9.20.9-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7v3h0hsyq17zl8wd7zpkzhif215ywagw-cyrus-sasl-2.1.28&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/004mzgs45wax9qlxrqpzhjbnzz049gsy-gsasl-2.2.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/039lh7h4fv88p1mxybhw35fz6y3y5mb3-libpq-17.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xam5b9xk11767mz35dc9i5gcmy9ggsaf-popt-1.19&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8vqqb9hp35whmp9fxd4c01z2zrdy8g5g-pre-switch-checks&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1l2x502h3j9bkp2ln3axm9qp70ibg7a1-qrencode-4.1.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hng18h8w0w3axygpknq9p9pn7yd0c1m5-rapidcheck-0-unstable-2023-12-14&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/971mpk4nqhqcxggx0yi60w9y1ya570bj-readline-8.2p13&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6vw0k4y7zxrbl3sikwbmn8aflzyi923q-s2n-tls-1.5.17&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mi0yqfw3ppyk0a4y6azvijaa4bmhg70y-system-sendmail-1.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v03zr9slrp64psxlpwh7gn0m5gcdglwm-systemd-minimal-libs-257.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qih5jc5im739yjgdslbswyxmz8kslqdl-perl5.40.0-Net-SMTP-SSL-1.04&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wcawvp0ilpqmmjfx8z6nbcsmcbpfa6i7-logrotate-3.22.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4iqgxg1ixmnvf8cq6jagz6ipas0p4bg5-tbb-2021.11.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c96bpmpg46wr7pq4ls8k56jrlysmz9nr-time-1.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2crk9xnq5x9v7yf0r2nwkgj8qsmxr4ly-pkg-config-wrapper-0.29.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pxflxwl6fa54jjc619fqdja5z4fn5p35-openldap-2.6.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7rdy1m9afs7036hwhf1r8lw1c900bmfb-php-pdo_pgsql-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hsnmgsywiz5izr59sm8q1fwcs64d8p85-php-pgsql-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xa5nkrg7h2akk0962c3k9hxly104yq0k-tree-sitter-0.25.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8y5hcryppj548yfx6akiw93qrw8zv6js-unbound-1.23.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/08inrjiy9snpmn77siczc0ncwpcbfv4v-unit-script-container_-post-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8afssqln4ckx4ii555ly707i2xvk17xy-unit-script-container_-pre-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xqmz2x2zmg6w76wl1b1kznv0b4x7dfr6-perl5.40.0-ExtUtils-PkgConfig-1.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jp25r6a51rfhnapv9lp8p00f2nzmfxxz-bind-9.20.9-host&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xzv4hkskh8di1mk7ik75rvbkyr7is882-guile-2.2.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/llcqfkdwbj1m1s4fbby82b49hffxqdb0-php-readline-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a7j3s4lqfa5pfrxlddmmkxx3vjz6mjzf-aws-c-io-0.15.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vj7inkvjyd3s0r30h4b5pq81f4jlkffr-tbb-2021.11.0-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lqn8cpyf4nq8704p7k3wjbym51q87rh3-unit-script-post-resume-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3wb1ngcfqajx6slx4c335lvb83js9csr-unit-script-pre-sleep-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/03pbln3nwbxc6ars4gwskgci3wj557yy-unit-script-prepare-kexec-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zf9xnwy0r9mzm3biig8b56hgyahfhf6b-unit-script-sshd-keygen-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2pvhq9kgqh5669qj6805vpasngivad8h-lvm2-2.03.31-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3z98iawifra8xn74bmdda6xbwgr5z0lh-unit-script-systemd-timesyncd-pre-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c5vpbrb9iiq9jynnx57f0h114qar1dkw-unixODBC-2.3.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/675r4l9rpmaxdanw0i48z4n7gzchngv7-util-linux-minimal-2.41-login&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0jj4mmc0861dqz2h603v76rny65mjidx-vim-9.1.1336-xxd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dqcl4f3r1z7ck24rh9dw2i6506g7wky5-which-2.23&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bbq0c28cvahc9236sp33swq4d3gqn2rc-xlsfonts-1.0.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n50daiwz9v6ijhw0inflrbdddq50k3sq-aws-c-event-stream-0.5.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7j95a3ykfjgagicfam6ga6gds2n45xc0-aws-c-http-0.9.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kqrqlcdqs48qslxsqsnygdyx32w7lpwg-php-ldap-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a232zjl9jnmhq56hfr5n5lz4qg5fpb83-xxHash-0.8.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/za3c1slqlz1gpm6ygzwnh3hd2f0lg31z-libblake3-1.8.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mzvz45f54a0r0zjjygvlzn6pidfkkwj3-audit-4.0.3-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s5cy3qgb3w0i1ylwm8dbsnk3m5jqxik4-m17n-db-1.8.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7d0871d6pn8r51sbpclclg56mmrq761a-nix-info&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xzfhjkn4am173n6klibs9ikvy1l08hfg-nixos-firewall-tool&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/i5323bb72x07y56d8z2iwb589g56k2y8-vim-9.1.1336&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pa60s415p92gnhv5ffz1bmfgzzfvhvd8-xz-5.8.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/srby6wmvg7dp454pwb6qvaxdiri38sc1-zlib-1.3.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/k8asbblj2xn748rslklcll68b4ygh2am-zlib-ng-2.2.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3p844hlrf7c7n8jpgp4y4kn9y4jffn4i-php-pdo_odbc-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/myzfn9vnyglhq3vj4wf99fi8qj98mqri-zlib-ng-2.2.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vcrjkcll3rnr95xjql8rz57gjlhh2267-zsh-5.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d8vq999dg607ha6718fimpakacfax0gd-zstd-1.5.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4rdbzw9g2vpyvs0b07pgmc1554pwdma4-aws-c-auth-0.8.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/k4xya9rihwkd175zxvcfnsqbzwrsgwmb-aws-c-mqtt-0.11.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/974a51073v6cb7cr5j0dazanxzmk9bxg-binutils-2.44-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a7h3ly9qzh8wk1vsycpdk69xp82dl5ry-cracklib-2.10.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p3sknfsxw0rjmxbbncal6830ic9bbaxv-audit-4.0.3-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rrnlyc5y7gd5b0f91a89vbw1flhnlm73-file-5.46&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yba197xwc8vvxv9wmcrs9bngmmgp5njb-gnutls-3.8.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4f7ssdb8qgaajl4pr1s1p77r51qsrb8y-kexec-tools-2.0.29&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fjnh5mgnlsahv2vsb8z1jh41ci924f7k-aws-c-s3-0.7.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5f0bv68v1sjrp4pnr8c6p7k04271659w-libfido2-1.15.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qvyvscqgr6vyqvmjdgxqa521myv5db0p-kmod-31&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/89bxhx3rhk6r4d5fvwaysrykpmvmgcnm-kmod-31-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v63bxfiacw082c7ijshf60alvvrpfxsq-binutils-2.44&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g91dviqva4rkkw8lw30zy3gj14c1p23s-libarchive-3.7.8-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dm19r683p4f07v2js5jnfnja5l296gs6-aws-crt-cpp-0.29.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jqhlcbmg1fsvc8w2w3ai9f9i8lzk7yfv-libgccjit-14.2.1.20250322&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y3x4m9wy3a731ibvgvs194j10znc392m-libpng-apng-1.6.46&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9642gi5dl4w9nkhab0l6xry685cg403c-libssh2-1.11.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yn4y14blp0j4l9044jxzjzf9i11kpjsx-libzip-1.11.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vrdwlbzr74ibnzcli2yl1nxg9jqmr237-linux-pam-1.6.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7m1s3j4inc333vynaahynfgda1284iyh-m17n-lib-1.8.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5i64l61if26whc3r9lzq6ycxpd2xnlgm-freetype-2.13.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zr0hlr1hybxs08j44l38b8na1m8xpkms-libwebp-1.5.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/v578vkzh0qhzczjvrzf64lqb2c74d5pk-curl-8.13.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hd1ys7pkiablfdgjvd1aq15k9jplsm2j-libgit2-1.9.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1dxfw2zshri809ddyfqllvff3cfj96ma-libmicrohttpd-1.0.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g81krn6p9fmyb2ymkd6d7cndjma3hzq0-etc-shells&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2vd9h77mrciiff8ldj1260qd6dlylpvh-nano-8.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9wlknpyvdm3n4sh6dkabs0za1n5nvfjn-aws-sdk-cpp-1.11.448&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s2np0ri22gq9pq0fnv3yqjsbsbmw16xi-curl-8.13.0-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cly4pxh7avd579girjmpxmx8z6ad4dyp-elfutils-0.192&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ldn53xpxivf489d7z673c95fkihs5l8r-fontconfig-2.16.0-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/vam5p76i7kbh1pwhdvlrhb33wgyfzy6x-chfn.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yr8x6yvh2nw8j8cqxana4kwn8qp9pjh2-chpasswd.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d4zhdmcqi6z247436jqahvz8v1khrcbi-chsh.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p83i191brxfj966zk8g7aljpb8ixqy1m-groupadd.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xlxar4qknywb8i3rf27g8v85l6vxlh2j-groupdel.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kkv7k9fmsfy5iljy4y2knirmrpkbplzs-groupmems.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/76gwx407dhh1ni9fn64h0yha3c1zwabp-groupmod.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qzk9gj8jdl37xqiccxa98g442byp3rrq-libtiff-4.7.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/64zabz1hxymxbcvp78hp9kacrygnf9l9-fontconfig-2.16.0-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ka4yf6hhsx1vlqkff4bvrvn27kbp28gg-mariadb-connector-c-3.3.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gs6syc444yjbg5ivf36sn535chg6mkrx-libXft-2.3.9&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bmmmy3sz3fmlxx64rlw1apm7ffywpyap-libpwquality-1.4.5-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3qk4g71ciq7hf06nmy252qf4ng36g0s7-nginx-1.28.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/i0nlz4mcyxzxd96x5dv0zcy23z6xkvzy-openssh-10.0p2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s1c3kiwdwcff03bzbikya9bszz45mmkc-etc-nanorc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/pn4js43jj8ag504ib4dyf5vd5ap2ilkg-libwebp-1.5.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gg124glj125xfc8jzvkl6r47ph8nl6pw-passwd.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/fx0cjyvqjmfnbqxcd60bwaf36ak16q2q-pciutils-3.13.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/al9x8cr5xifp3qd2f5cdzh6z603kb5ps-perl-5.40.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ls9jrqk9arnwrm3cmm1gd9wgllpn4b3b-php-curl-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7hnpi2q3cxfzkzh7miv5rkl4b74gpzk4-php-imap-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cq6kbdji0q5c3r2m0ikaaiip5z0p6318-php-mysqlnd-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/w0nkgg89ls4xvk49lnv483blmhq2ac9x-php-zip-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/h1z0wlyb4av929a6qkxblhndha0d6byn-php-zlib-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7f3nwfvk0f32663rz1xn38cbsl66idx2-libbpf-1.5.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mww31587ng38jw87pf1dv121ih27clf5-plocate-1.1.23&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sb4ml8qjxcr2idzdgjcw2bz11p6nzff4-rsync-3.4.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hhfm5fkvb1alg1np5a69m2qlcjqhr062-binutils-wrapper-2.44&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7nln4vh5kbwba6q9d3ga77vk2vj72mdk-runuser-l.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qb0d1xc8vxdr8s2bkp4q8msj8bhkvmg8-runuser.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wgq5kj4qhi78sr70mwj3bgnmx4ya87fr-security-wrapper-unix_chkpwd-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c0clf3w4bfkcg9fc7nl6bfhgivz24wvc-shishi-1.0.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yfjzkkkyxcalyj7l1n4d4y6s81i65hmy-sqlite-3.48.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x0ncvjhy2vgz174bhm8yycywwrjvgr9a-strace-6.14&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ywy0hjiydvv561a5wds6ba7z059zj9im-sudo-1.9.16p2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qywjdkbap2h7g17qrzhi4nm243cqpx1f-sudo.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dk55smr7wdjad151r7cv1pln0winqq9x-tcb-1.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g4d8pli27k90s0n4nnm5nipxbyrcd9vl-useradd.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/71v1svlxdziiqy8qmzki3wsrg7yv7ybq-userdel.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/npkqihnvfw9cx3a1mzr59x23vkqql51g-sshd.conf-final&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bxznmkg59a4s2p559fmbizc2qcgjr3ny-iproute2-6.14.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bqa6kwd5ds2jrj76nch6ixdvzzcy4sxl-usermod.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/b895xnbwyfj1msj6ljcsvwfdhwqhd2vd-shadow-4.17.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zx9qxw749wmla1fad93al7yw2mg1jvzf-vlock.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z21r5fak3raias1zlc0grawnsrcq094x-X-Restart-Triggers-sshd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/98zamhd8d0jq3skqwz28dlgph94mrqir-xz-5.8.1-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/l9xn7mbn0wh0z7swfcfj1n56byvcrisw-zstd-1.5.7-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/clfkfybsfi0ihp7hjkz4dkgphj7yy0l4-nix-2.28.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yz7bsddsmyssnylilblxr8gxyaijfis7-php-pdo_sqlite-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rmj2j70y96zfnl2bkgczc1jjxxp1gpc2-php-sqlite3-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5c38fjjwfnlfjiiq62qyrr545q0n60ki-util-linux-2.41-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/r03ly1w54924k8fag1dhjl3yrllj6czd-util-linux-minimal-2.41-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mmz4qa42fhacp04wfjhwlslnlfffyxjv-append-initrd-secrets&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9r81a64smasyz3j7x3ah684hyzivmplx-kbd-2.7.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/324bqqlvdjbsixcbagdn8yjxc6zcj28a-security-wrapper-newgidmap-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mvgsv5643miclpcpwzv43kibj5ydpxvl-security-wrapper-newgrp-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/n42x8ly03p2dyj6lqnmaynrcw8mg72d7-gss-1.0.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xrdkznkvi79w8pp1cyhzi40prmxilw8y-security-wrapper-newuidmap-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p7vixy3km13dwf3g4rkg9n3qwkj2vhik-security-wrapper-sg-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2adjiqpm8p55hfhhrw3f1kvi340allma-security-wrapper-sudo-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0b1qa8fm793qvcn8bvr5kg5jl4indh9y-security-wrapper-sudoedit-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4hjw4c56ml09jbac2mzz38qc958d3fb2-shadow-4.17.4-su&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m4w8d2h3v76anng7s9cv9c1iq9w6y2jj-cryptsetup-2.7.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jf0v9bq4dlk56acbkpq6i84zwjg4g466-e2fsprogs-1.47.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bkpj51fz88rbyjd60i6lrp0xdax1b24g-glib-2.84.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/170jn0hjz46hab3376z1fj79vmn0nynm-libSM-1.2.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8w718rm43x7z73xhw9d6vh8s4snrq67h-python3-3.12.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/a885zzx9s5y8dxbfvahwdcwcx6pdzm9q-tpm2-tss-4.1.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m2dkj8xcpcrymd4f4p46c3m59670cj9y-security-wrapper-su-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/d4icl77wfbz3y5py1yni18nmqwkrb4lr-libSM-1.2.5-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2rxzdljx3dp4cgj1xlald496gdsjnwj8-libXt-1.3.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/agpxymqp96k4bksyz3bbzr5y8jgykf4p-util-linux-minimal-2.41-mount&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sjsapivqvz7hs93rbh1blcd7p91yvzk1-console-env&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jws80m7djgv03chq0ylw7vmv3vqsbvgg-util-linux-minimal-2.41-swap&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2050009wgldpv3lxld3acz5pr6cr7x53-wget-1.25.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/77z9fh96318kyjmmidi558hyyssv00s8-bcache-tools-1.0.8&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lg0d9891d12dl3n1nm68anmlf3wczf28-btrfs-progs-6.14&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cx6fbilhj4nmq9dl8c8c73mimm08x60z-e2fsprogs-1.47.2-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hlmmf01lhg62fpqhzispzs8rhzn7gg4p-libXmu-1.2.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wfxr783my1pr6pnzd6x22dpi8amjwkkd-X-Restart-Triggers-reload-systemd-vconsole-setup&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m506rljkkpxc4d0j0j41qjhldqrwxz4x-libXt-1.3.1-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9qqln0vxf1g6ll2wpkdfa2cmpm4nn17y-libXaw-1.0.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/if9z6wmzmb07j63c02mvfkhn1mw1w5p4-systemd-257.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/ykzprjkb2l61gnlcm368vh8wnj7adwx6-systemd-minimal-257.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mwan3006nzdq6ia8lw3hyk4vlc585g17-libXmu-1.2.1-dev&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1nxchlxi7i0b1nhsyq732al8sm1blywm-util-linux-2.41-login&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mhibs2q4f3mjpzwgm6wdk2c4d6vkaklv-Xaw3d-1.6.6&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g51ca42mmgxzz7xngf0jzhwd4whi19lj-util-linux-2.41-mount&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8m86a49g1p7fvqiazi5cdmb386z7w5zf-libotf-0.9.16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zma6jllb9xn22i98jy9n8mz3wld9njwk-util-linux-2.41-swap&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mw6bvyrwv9mk36knn65r80zp8clnw9jl-util-linux-minimal-2.41-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/94jfyay8h0dwbakr69b91rsf8pdvah05-xauth-1.1.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j3h72p435glzylc2qjny8lqd4gml03ym-xrdb-1.2.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zpprhp27r6chnkfkb85wl42p33vsawj8-su.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/41qbms27n02859ja4sc7wsd9mfp3ward-cairo-1.18.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/8bsl1vrab2pwj8ilpzfn2iwzbrps8jgq-harfbuzz-10.2.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j61kasrhqidgpj9l9zb1wvnizk1bsiqf-qemu-host-cpu-only-9.2.3-ga&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cdr8agx3ffy086z30wiysnclrc5m8x69-gdk-pixbuf-2.42.12&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/b5xcfdnccfslm89c8kd3lajgb5drx3h4-shared-mime-info-2.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y7gbjn3x388syav1bjzciia9ppia2zqw-urxvt-font-size-1.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/hb6l900n8qiaxg0zj6l20yy7bn9ghxp3-wmctrl-1.07&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/808xr68djvk0x3r754mi81yvm2yr9ppq-libavif-1.2.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y5bgh0pyxzcgp90ywmgl9dk2m1j3hcbr-urxvt-perl-unstable-2015-01-16&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/z0kbsgnma0mijn5ssqfi3dk9z28bqlwj-pango-1.56.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/lzj596ffj1xk0r9v9l4gpgwg9w8jb0fr-check-mountpoints&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/5q42cwjbqj7ir7pvdqn411bbzr304g2j-etc-systemd-system.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7c0l3jk0fszisqidxrc2bby99dv5d261-fuse-2.9.9-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/nmyh57dqf1v6l6swghywkrb63aqmzzh8-fuse-3.16.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dh54wizfsivqa4ygx76jn49lpxkqbaf6-lvm2-2.03.31-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zp3db1aj7gs7p73wkm9v76x36z641nsi-man-db-2.13.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qcf53qls5h6jk0czdiwdwncfvfnvfmpb-gd-2.3.3&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9z8dbw37f5b6nrmh07310g1b2kdcs8sf-nixos-enter&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/14spdmgq38vmzywkkm65s65ab6923y6p-librsvg-2.60.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/csx6axnwacbq8ypl375p10why1fc2z8p-security-wrapper-fusermount-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j1d4jkh31x2yq5c8pibjifwcm5apa06l-fuse-3.16.2-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/j3jbm4d3hmz0nh4z3pqfy68zgil8immv-nixos-install&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/0r8953vg0n1b38d0jkk9lgbjfxvf8yc4-php-gd-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7hg38dsdzfk0jnb9q3q77ql9q1chp4fz-nixos-option&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/mz9qpdl066bzg4n3rzb7x82dmx5jy386-security-wrapper-fusermount3-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/sm4b1vl7578rl2yiss62acs7ls7qinad-lvm2-2.03.31&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gxsa0wrpl9r1vl2zp3s1vkhmdf8ia0ca-php-extra-init-8.1.32.ini&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yi0knhi2qccafj49a8yd76rizllzx7bd-dbus-1.14.10-lib&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rys6134aqazihxi4g5ayc0ky829v7mf0-dbus-1.14.10&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/75m2zly9vl6gvx3gc23y7hgjsbarqf7r-switch-to-configuration-0.1.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6kldkgh0i8h6wwfi78nviki6a15h03bw-perl-5.40.0-env&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rqy3y1p2c1acfnbhkxzpixdshnivqaxl-perl-5.40.0-env&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/zql0aksg8vpmaivh4ylkzg8ky4k1r3ms-perl-5.40.0-env&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/4ccfn37h8jfpppsi2i0rx0dx9c73qmsa-perl5.40.0-DBI-1.644&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gf1gs0w896yg73wyphgwdzhwa08ryw3n-perl5.40.0-String-ShellQuote-1.04&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p90lckzsmp16zh0rfx7pfc6ryf77y3c6-perl5.40.0-libwww-perl-6.72&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/20f0f68rsai61a7rkcy6zxl6c0vh1z41-perl5.40.0-urxvt-bidi-2.15&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/g5lvfibif6crcl82mmzwicq6xwv9dcvf-rxvt-unicode-unwrapped-9.31&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kdrbnjhy3wchgbpkiz486k0qcv5z9a07-rxvt-unicode-vtwheel-0.3.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xj5y2ng1jbpx99nzi2pjajs5pdjn07rg-security-wrapper-dbus-daemon-launch-helper-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6v5a3nd0fxwddy5rlgl02hx7qmmb14ky-texinfo-interactive-7.2&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s8lhl3z9z2jjaq1qschc4g0wd3dy91im-w3m-0.5.3+git20230121&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/50piw9b7b80vfjf9yny54zxfgjx3f3va-etc-ssh-ssh_config&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/dkx6gwpq53a80aya87fi1vs43pr42s91-etc-sysctl.d-60-nixos.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/27xwi66r3zx83cfr2p4nz4d3p8q5mvcd-htop-3.4.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p6z4ag3v1a3bmdd7b2ga8n2s53r3rb7s-login.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/q9idyw3m487xfb10dyk4v773kcyzq2da-php-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/x3bxjpkcbfyzmy5695g1cchf04fbz8ca-procps-4.0.4&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/3wikk2w5zb68g0j90xqcqbn4dhq59910-nixos-generate-config&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/54a3gbciba6is1fvi29k291v04hkgihb-X-Restart-Triggers-systemd-sysctl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6pgj3ja7zvlahqbcycd43iyc4g498ki0-perl5.40.0-DBD-SQLite-1.74&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/qvznfa46sqccjdh8vlnpzpqfkqh58s2j-sshd.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gsman0cwlms2l679bla5vgmf21jc5lvl-systemd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/q8ycjc7hnjm71p4n106acywcdsjjpskl-systemd-user.pam&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kyf94km34b9ydzy33gvrvdd893py5pc5-rxvt-unicode-9.31&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/bvvqvfbh0wq04di5f3lkrzjqy5pvq4w3-unit-script-container_-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/af291yai47szhz3miviwslzrjqky31xw-util-linux-2.41-bin&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/iqqhya38s39vgh1bk4v5sr6jvrmi5sg3-nixos-help&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/jrrzha35h0bxbp2h30nv4dpa0fk4qhgb-perl-5.40.0-env&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rmnxnpxvm1wmlmgh5krgdf9wrym5ks99-tailscale-1.82.5&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/xc3zdwldi1bbsrvjjvix7s57s31hsv29-command-not-found&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rhmacziivxfjs8chklcbm37p59wih6sw-nixos-help&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/7rjs1gm1377hsbd5yqg5bii3ay3f75q7-etc-bashrc&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/s674qd2b7v163k38imvnp3zafzh0585n-50-coredump.conf&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m4qaar099vcj0dgq4xdvhlbc8z4v9m22-getty&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rr6bdh3pdsvwjrm5wd32p2yzsz16q6z2-security-wrapper-mount-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/2q4yksm7gqgszl9axs95ylwakwk9yb8w-security-wrapper-umount-x86_64-unknown-linux-musl&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/gmydihdyaskbwkqwkn5w8yjh9nzjz56p-udev-path&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/yaz54h00w6qv85lw40g0s0dw3s4s53ws-unit-script-nixos-activation-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/9jp02i4p4lrxz51sxiyhz71shr9vb6bc-mount-pstore.sh&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/csm3q68n81162ykn3wibzh0fs4fm0dhk-nixos-container&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/cflf8pxlaapivg98457bwh0nh1hasf5h-nixos-rebuild&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rajz07kxw9xj94bi90yy0m2ksgh3wprf-reload-container&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/c2c364mdd4qj6c51bjs6s3g4hb42c0ia-getty&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/298j97sm5jr2x5z8w5q8s3mzzpb3rjjw-unit-script-suid-sgid-wrappers-start&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/p1j5bc30pbq6bqpw2d676azqdv4whdi5-udev-rules&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/rrnq5j9c6j39k4pk9xkk4913h4zsqf5b-php-with-extensions-8.1.32&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/m2rqmjkvdd86lb4i8mi3rafxggf9l2py-X-Restart-Triggers-systemd-udevd&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/805a5wv1cyah5awij184yfad1ksmbh9f-git-2.49.0&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/wk8wmjhlak6vgc29clcfr1dpwv06j2hn-mailutils-3.18&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/kmwlm9nmvszrcacs69fj7hwpvd7wwb5w-emacs-30.1&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/y0aw1y9ggb4pyvhwk97whmwyjadivxny-linux-6.12.30-modules&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/1i4iz3z0b4f4qmbd9shs5slgfihs88vc-firmware&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/18f0xc0gid1ma6yjjx5afny9lnji3hf0-etc-modprobe.d-firmware.conf&#39; from &#39;https://cache.nixos.org&#39;...
### Installing NixOS ###
Pseudo-terminal will not be allocated because stdin is not a terminal.
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
installing the boot loader...
setting up /etc...
Created &#34;/boot/EFI&#34;.
Created &#34;/boot/EFI/systemd&#34;.
Created &#34;/boot/EFI/BOOT&#34;.
Created &#34;/boot/loader&#34;.
Created &#34;/boot/loader/keys&#34;.
Created &#34;/boot/loader/entries&#34;.
Created &#34;/boot/EFI/Linux&#34;.
Copied &#34;/nix/store/if9z6wmzmb07j63c02mvfkhn1mw1w5p4-systemd-257.5/lib/systemd/boot/efi/systemd-bootx64.efi&#34; to &#34;/boot/EFI/systemd/systemd-bootx64.efi&#34;.
Copied &#34;/nix/store/if9z6wmzmb07j63c02mvfkhn1mw1w5p4-systemd-257.5/lib/systemd/boot/efi/systemd-bootx64.efi&#34; to &#34;/boot/EFI/BOOT/BOOTX64.EFI&#34;.
Random seed file /boot/loader/random-seed successfully written (32 bytes).
Created EFI boot entry &#34;Linux Boot Manager&#34;.
installation finished!
### Rebooting ###
Pseudo-terminal will not be allocated because stdin is not a terminal.
Warning: Permanently added &#39;10.25.0.87&#39; (ED25519) to the list of known hosts.
### Waiting for the machine to become unreachable due to reboot ###
kex_exchange_identification: read: Connection reset by peer
Connection reset by 10.25.0.87 port 22
### Done! ###
nix run github:nix-community/nixos-anywhere -- --flake .#wiki    --target-hos  6,58s user 2,98s system 17% cpu 55,063 total
</code></pre></details>
<h2 id="post-installation-steps">Post-Installation Steps</h2>
<p>Now that the declarative part of the system is in place, we need to take care of
the stateful part.</p>
<p>In my case, the only stateful part that needs setting up is the Tailscale mesh VPN.</p>
<p>To set up Tailscale, I log in via SSH and run <code>sudo tailscale up</code>. Then, I add
the new node to my network by following the link. Afterwards, in the <a href="https://login.tailscale.com/admin/machines">Tailscale
Machines console</a>, I disable key
expiration and add ACL tags.</p>
<h2 id="making-changes">Making Changes</h2>
<p>Now, after I changed something in my configuration file, I use <code>nixos-rebuild</code>
remotely to roll out the change to my NixOS system:</p>
<pre tabindex="0"><code>% nix run nixpkgs#nixos-rebuild -- \
  --target-host michael@zammadn \
  --use-remote-sudo \
  switch \
  --flake .#zammadn
</code></pre><p>Note that not all changes are fully applied as part of <code>nixos-rebuild switch</code>:
while systemd services are generally restarted, newly required kernel modules
are not automatically loaded (e.g. after enabling the <code>edgetpu</code> coral hardware
accelerator in Frigate).</p>
<p>So, to be sure everything took effect, <code>reboot</code> your system after deploying
changes.</p>
<p>One of the advantages of NixOS is that in the boot menu, you can select which
generation of the system you want to run. If the latest change broke something,
you can quickly reboot into the previous generation to undo that change. Of
course, you can also undo the configuration change and deploy a new generation —
whichever is more convenient in the situation.</p>
<h2 id="conclusion">Conclusion</h2>
<p>With this article, I hope I could convey what I wish someone would have told me
when I started using Nix and NixOS:</p>
<ol>
<li>Enable flakes and the new CLI.</li>
<li>Use nixos-anywhere to install remotely.
<ul>
<li>Build a custom installer if you want, it’s easy!</li>
</ul>
</li>
<li>Use <code>nixos-rebuild</code>’s builtin <code>--target-host</code> flag for remote deployment.</li>
</ol>
<p>Where do you go from here?</p>
<ul>
<li>Read through all documentation on <a href="https://nixos.org/learn/">nixos.org →
Learn</a>.</li>
<li>Here are a couple of posts from people in and around my bubble that I looked
at for inspiration / reference, in no particular order:
<ul>
<li>Michael Lynch wrote about <a href="https://mtlynch.io/notes/nix-oracle-cloud/">setting up an Oracle Cloud VM with
NixOS</a> and about <a href="https://mtlynch.io/notes/zig-vscode-nix/">managing his
Zig configuration</a>.</li>
<li>Nelson Elhage wrote about using Nix to test dozens of Python interpreters
as part of his <a href="https://blog.nelhage.com/post/cpython-tail-call/">performance investigation into Python 3.14 tail-call
interpreter
performance</a>.</li>
<li>Vincent Bernat wrote about <a href="https://vincent.bernat.ch/en/blog/2025-offline-pki-yubikeys">using Nix to build an SD card image for an ARM
single board
computer</a>.</li>
<li>Mitchell Hashimoto shared <a href="https://github.com/mitchellh/nixos-config/">his extensive NixOS
configs</a>.</li>
<li>Wolfgang has a <a href="https://www.youtube.com/watch?v=f-x5cB6qCzA&amp;t=1036s&amp;pp=ygUOV29sZmdhbmcgbml4b3M%3D">YouTube video about using NixOS for his Home
Server</a>
(<a href="https://github.com/notthebee/nix-config">→ his configs</a>)</li>
</ul>
</li>
<li>Contact your local Nix community! I recently attended the <a href="https://zurich.nix.ug/">“Zero Hydra
Failures” event of the Nix Zürich group</a> and the kind
people there were happy to talk about all things Nix :)</li>
</ul>
]]></content>
  </entry>
</feed>
