<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Nixos on Andrei Mahalean&#39;s Weblog</title>
    <link>https://blog.maha.nz/tags/nixos/</link>
    <description>Recent content in Nixos on Andrei Mahalean&#39;s Weblog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-US</language>
    <copyright>Andrei Mahalean (CC BY 4.0)</copyright>
    <lastBuildDate>Mon, 30 Mar 2026 00:00:00 +1300</lastBuildDate>
    <atom:link href="https://blog.maha.nz/tags/nixos/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Nixos Part 1</title>
      <link>https://blog.maha.nz/posts/nixos-part-1/</link>
      <pubDate>Mon, 30 Mar 2026 00:00:00 +1300</pubDate>
      <guid>https://blog.maha.nz/posts/nixos-part-1/</guid>
      <description>&lt;h1 id=&#34;why-nixos&#34;&gt;Why NixOS?&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Series:&lt;/strong&gt; My NixOS Homelab: Part 1 of 10&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Traditional Linux servers accumulate changes over time until no one really knows what they&amp;rsquo;re running. NixOS fixes this by making your entire system configuration a file you can read, commit to git, and roll back at will. The learning curve is steep and the docs aren&amp;rsquo;t great, but it&amp;rsquo;s worth it. This series is the story of how I got here.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h1 id="why-nixos">Why NixOS?</h1>
<blockquote>
<p><strong>Series:</strong> My NixOS Homelab: Part 1 of 10</p>
<p><strong>TL;DR:</strong> Traditional Linux servers accumulate changes over time until no one really knows what they&rsquo;re running. NixOS fixes this by making your entire system configuration a file you can read, commit to git, and roll back at will. The learning curve is steep and the docs aren&rsquo;t great, but it&rsquo;s worth it. This series is the story of how I got here.</p>
</blockquote>
<hr>
<h2 id="how-i-got-here">How I Got Here</h2>
<p>My homelab went through a few iterations before landing on NixOS.</p>
<p>First it was the obvious thing: install Ubuntu, <code>apt install</code> everything, configure it by hand. It worked. For a while.</p>
<p>Then I moved to Docker Compose. Services were more portable, easier to version, easier to reproduce. Better, but the host OS was still a snowflake. Every tweak I&rsquo;d made to the underlying system was stored only in my memory and a pile of shell history.</p>
<p>Then I tried <a href="https://github.com/davestephens/ansible-nas">ansible-nas</a>, which at least acknowledged the problem. The idea was right: describe your homelab as code, run a playbook, get a configured system. But it still felt wrong. Ansible describes <em>actions</em> to take, not the <em>state</em> you want. Run the same playbook twice and you&rsquo;re hoping the idempotency checks hold. The underlying system could still drift between runs.</p>
<p>The moment that crystallised it: a disk died, and I had to rebuild from scratch using ansible-nas. The playbook ran. But it wasn&rsquo;t a complete picture of what I&rsquo;d had. Things needed fixing manually. I can&rsquo;t even remember exactly what. Which is precisely the problem. If you can&rsquo;t remember what your system needs to be correct, you don&rsquo;t have a declarative system. You have a script with amnesia.</p>
<p>There had to be a better way.</p>
<hr>
<h2 id="i-tried-nixos-before-it-didnt-stick">I Tried NixOS Before. It Didn&rsquo;t Stick.</h2>
<p>I&rsquo;d actually tried NixOS years earlier, on a laptop. It did not go well.</p>
<p>The learning curve was brutal. The documentation was (and honestly, still is) not great. Nix the language has a unique syntax that nothing prepares you for. And the whole Flakes situation (the feature that makes NixOS properly reproducible) was labelled &ldquo;experimental&rdquo;, which at the time made me nervous enough to avoid it. I gave up and went back to something that just worked.</p>
<p>What changed was a <a href="https://news.ycombinator.com/item?id=47479751">HN thread</a> where a bunch of people made the case that NixOS pairs particularly well with LLMs. The argument: because NixOS config is a declarative language with a massive, well-structured package repository, an LLM can actually reason about it (I am aware LLMs don&rsquo;t actually <em>think</em> but for the sake of the conversation, we&rsquo;ll go with that word). It can look up available options in nixpkgs, write correct module config, and catch mistakes in ways that are much harder with imperative shell scripts.</p>
<p>I decided to try again, this time leaning into that. The workflow I landed on: plan changes with Claude Opus (for the design thinking), implement with Claude Sonnet (for the actual Nix config), review the diff myself before applying. This is not a post about AI-assisted infrastructure, but it&rsquo;s worth being honest that the tooling is part of why it finally clicked.</p>
<h2 id="nixos-your-entire-system-is-a-file">NixOS: Your Entire System Is a File</h2>
<p>NixOS takes a radically different approach to the problem. Instead of running commands that mutate system state, you declare what your system should look like in a set of configuration files. Then you apply that configuration, and NixOS makes it so.</p>
<p>The entire state of my server (every installed package, every service, every systemd unit, every user, every open port) is described in a git repository. To understand what my server is running, I read the config. To change something, I edit a file and run <code>nixos-rebuild switch</code>. To undo that change, I run <code>nixos-rebuild switch --rollback</code> and I&rsquo;m back to exactly where I was before.</p>
<p>It sounds almost too good, so let me be clear: NixOS is not easy. The learning curve is real. The Nix language takes getting used to. The ecosystem has rough edges. There are times you&rsquo;ll spend an afternoon solving a problem that would&rsquo;ve taken five minutes on Ubuntu.</p>
<p>But once it clicks, it changes how you think about infrastructure. Going back to imperative config feels like giving up something important.</p>
<h2 id="the-mental-model-shift">The Mental Model Shift</h2>
<p>Before NixOS makes sense, you need to internalise one idea:</p>
<blockquote>
<p><strong>NixOS doesn&rsquo;t modify your system. It builds a new one and switches to it.</strong></p>
</blockquote>
<p>When you run <code>nixos-rebuild switch</code>, Nix evaluates your configuration, builds every package and config file from scratch in an immutable store (<code>/nix/store</code>), and then atomically switches the running system to that new generation. Your previous configuration still exists. It&rsquo;s still in the boot menu. You can switch back to it instantly.</p>
<p>This has a few profound consequences:</p>
<p><strong>There is no &ldquo;system state&rdquo; outside your config.</strong> Anything you set up by hand outside of your Nix config doesn&rsquo;t survive a rebuild. This sounds punishing at first. It&rsquo;s actually liberating. It forces you to commit changes to config rather than letting them drift into the void.</p>
<p><strong>Every package is content-addressed and isolated.</strong> Two packages can depend on different versions of a library, both installed simultaneously, with zero conflict. The infamous &ldquo;dependency hell&rdquo; problem is just&hellip; gone.</p>
<p><strong>Rollbacks are instantaneous.</strong> Because each <code>nixos-rebuild switch</code> produces a new system generation and the old one is kept, rolling back means rebooting and picking the previous entry from GRUB, or running <code>nixos-rebuild switch --rollback</code> without even rebooting.</p>
<p><strong>Reproducibility is built in.</strong> With Nix Flakes (which we&rsquo;ll use from day one in this series), every input to your system (nixpkgs itself, every community module, every tool) is pinned to an exact commit hash. Your <code>flake.lock</code> is a complete bill of materials for your system. Check it into git and you can rebuild the exact same system six months from now on entirely different hardware.</p>
<hr>
<h2 id="why-not-just-use-ansible">Why Not Just Use Ansible?</h2>
<p>Fair question. I tried that (via ansible-nas, specifically).</p>
<p><strong>Ansible</strong> brings idempotence and repeatability, but it&rsquo;s still fundamentally imperative. It describes <em>actions</em> to take, not the desired <em>state</em> of the system. Run the same playbook twice and you&rsquo;re hoping the authors wrote proper idempotency checks everywhere. The underlying system packages are still managed by the distro&rsquo;s package manager and can drift between runs. When I had to rebuild after a disk failure, ansible-nas got me close, but not all the way there.</p>
<p><strong>Docker Compose / Podman</strong> solves the &ldquo;which version of this app&rdquo; problem for containerised workloads, but your host system is still a snowflake. The kernel, system packages, networking configuration, secrets management. All of that lives outside the containers and can drift.</p>
<p><strong>NixOS</strong> manages everything: the host OS, the kernel, system services, user packages, dotfiles via Home Manager, and containers too if you want them. The whole stack.</p>
<hr>
<h2 id="what-were-building-meet-bunk">What We&rsquo;re Building: Meet <code>bunk</code></h2>
<p>Throughout this series, I&rsquo;ll be working with a real machine: a homelab server called <code>bunk</code>.</p>
<p><strong>Hardware:</strong></p>
<ul>
<li>AMD Ryzen CPU</li>
<li>Gigabyte GA-B450M-S2H motherboard</li>
<li>465 GB NVMe OS drive</li>
<li>4 × spinning HDDs in two ZFS mirror pools (~6 TB usable)</li>
</ul>
<p><strong>Services running (all declared in Nix config):</strong></p>
<table>
  <thead>
      <tr>
          <th>Category</th>
          <th>Services</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Media</strong></td>
          <td>Jellyfin, Sonarr, Radarr, Prowlarr, Bazarr, NZBGet, Transmission</td>
      </tr>
      <tr>
          <td><strong>Photos</strong></td>
          <td>Immich (with ML face recognition)</td>
      </tr>
      <tr>
          <td><strong>Documents</strong></td>
          <td>Paperless-ngx</td>
      </tr>
      <tr>
          <td><strong>Books</strong></td>
          <td>Calibre-Web, Shelfmark</td>
      </tr>
      <tr>
          <td><strong>Finance</strong></td>
          <td>Actual Budget</td>
      </tr>
      <tr>
          <td><strong>Productivity</strong></td>
          <td>Vikunja, Mealie, n8n</td>
      </tr>
      <tr>
          <td><strong>Monitoring</strong></td>
          <td>Uptime Kuma, Scrutiny</td>
      </tr>
      <tr>
          <td><strong>Infrastructure</strong></td>
          <td>Caddy, Authelia, Podman, NFS, Restic, BorgBackup</td>
      </tr>
  </tbody>
</table>
<p>That&rsquo;s 30+ services, all declared in Nix, all backed up to two separate cloud providers. The system configuration lives in a git repository and rebuilding from scratch is one command.</p>
<p>One honest caveat: the <em>system</em> is declarative, but application-level configuration is not always. Some services can be configured entirely through NixOS module options. Some accept environment variables. Some require you to click through a setup wizard on first run and store their state in a database. I think of it in three tiers: NixOS module options where possible, environment variables for secrets and simple settings, and Click-Ops as a last resort. The Click-Ops stuff is backed up offsite and I can roll back the system around it, so I&rsquo;m comfortable with that trade-off. I revisit it once a year to see if anything can move up the chain.</p>
<p>Not all services support SSO either. Some won&rsquo;t let you disable their built-in auth at all, and others lack OIDC support entirely. For those, Authelia sits out and the app handles its own authentication. It&rsquo;s not perfectly uniform, but it works.</p>
<hr>
<h2 id="the-core-concepts-youll-need">The Core Concepts You&rsquo;ll Need</h2>
<p>Before diving into installation in Part 2, here are the key ideas that will come up repeatedly:</p>
<h3 id="nix-the-language">Nix (the language)</h3>
<p>A purely functional, lazily-evaluated language used to describe packages and configurations. It has an unusual syntax that takes some getting used to. You don&rsquo;t need to master it to be productive, but you do need to be comfortable reading it.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="c1"># A simple NixOS service declaration</span>
</span></span><span class="line"><span class="cl"><span class="n">services</span><span class="o">.</span><span class="n">caddy</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">virtualHosts</span><span class="o">.</span><span class="s2">&#34;jellyfin.maha.nz&#34;</span><span class="o">.</span><span class="n">extraConfig</span> <span class="o">=</span> <span class="s1">&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">    reverse_proxy localhost:8096
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div><h3 id="nixpkgs">Nixpkgs</h3>
<p>One of the largest package repositories in existence, with over 100,000 packages. It&rsquo;s also the source of NixOS module definitions: those <code>services.*</code> and <code>programs.*</code> options that let you configure system services declaratively.</p>
<h3 id="nix-flakes">Nix Flakes</h3>
<p>An experimental (but widely adopted) feature that pins all your Nix inputs to exact versions, making builds fully reproducible. We&rsquo;ll use flakes from day one. Think of <code>flake.nix</code> as <code>package.json</code> and <code>flake.lock</code> as <code>package-lock.json</code>, but for your entire operating system.</p>
<h3 id="home-manager">Home Manager</h3>
<p>A Nix-based tool for managing user-level configuration (dotfiles, user packages, shell config). We&rsquo;ll use it as a NixOS module so user and system config live in the same git repo.</p>
<h3 id="generations">Generations</h3>
<p>Each time you run <code>nixos-rebuild switch</code>, NixOS creates a new <strong>generation</strong> (a snapshot of your system configuration). You can list them with <code>nix-env --list-generations</code>, boot into any of them from the bootloader, and roll back instantly. Old generations are garbage-collected when you run <code>nix-collect-garbage</code>.</p>
<hr>
<h2 id="the-trade-offs-lets-be-honest">The Trade-offs (Let&rsquo;s Be Honest)</h2>
<p>NixOS is not for everyone. Here&rsquo;s what you&rsquo;re signing up for:</p>
<p><strong>The Nix language is genuinely weird.</strong> It&rsquo;s not Python or YAML or HCL. It&rsquo;s a functional language with lazy evaluation, and some concepts (like <code>lib.mkIf</code>, <code>lib.optionalAttrs</code>, overlays) take real effort to understand if you are not that smart like me.</p>
<p><strong>Error messages can be cryptic.</strong> When your config fails to evaluate, the error output is sometimes helpful and sometimes looks like it was generated by a Turing machine having an existential crisis.</p>
<p><strong>The ecosystem moves fast.</strong> Nixpkgs is massive and well-maintained, but NixOS-specific modules vary in quality. Some services have beautifully-designed modules with every option exposed. Others have bare-bones modules that still require manual workarounds.</p>
<p><strong>First-time setup is slower.</strong> Doing what takes ten minutes on Ubuntu might take an hour on NixOS the first time, as you figure out the right module option or why your service won&rsquo;t start.</p>
<p><strong>The payoff is real.</strong> After the initial investment, day-to-day operations become remarkably smooth. Adding a new service is adding a few lines to a config file. Upgrading the whole system is <code>nix flake update &amp;&amp; nixos-rebuild switch</code>. Breaking something is a five-second rollback.</p>
<hr>
<h2 id="whats-coming-in-this-series">What&rsquo;s Coming in This Series</h2>
<p>Here&rsquo;s the roadmap:</p>
<ol>
<li><strong>Part 1 (this post):</strong> Why NixOS?</li>
<li><strong>Part 2:</strong> Installing NixOS with Flakes and ZFS from day one</li>
<li><strong>Part 3:</strong> Structuring a config that doesn&rsquo;t fall apart</li>
<li><strong>Part 4:</strong> Secrets management with agenix (encrypted config in git)</li>
<li><strong>Part 5:</strong> Caddy + Authelia: SSO for every service</li>
<li><strong>Part 6:</strong> Self-hosting a media stack the NixOS way</li>
<li><strong>Part 7:</strong> Running containers when NixOS modules don&rsquo;t exist</li>
<li><strong>Part 8:</strong> Backups with Restic + BorgBackup</li>
<li><strong>Part 9:</strong> Monitoring, alerting, and knowing when things break</li>
<li><strong>Part 10:</strong> Deploying, updating, and rolling back without fear</li>
</ol>
<p>Each article is written to be useful on its own, but they build on each other. If you&rsquo;re starting from scratch, I&rsquo;d recommend reading in order. If you&rsquo;re already running NixOS and just want to know how I&rsquo;ve handled secrets or backups, jump ahead.</p>
<p>The config repo is private, but all the relevant examples will be in the posts themselves. I&rsquo;ll include real snippets throughout the series.</p>
<hr>
<h2 id="should-you-use-nixos">Should You Use NixOS?</h2>
<p>If any of these sound familiar, NixOS is probably worth your time:</p>
<ul>
<li>You&rsquo;ve ever broken a server and thought &ldquo;I have no idea how I set this up 2 years ago.&rdquo; (Maybe that says something about my appetite for documenting my own stuff)</li>
<li>You&rsquo;ve ever avoided upgrading a server because you were scared of what might break</li>
<li>You care about reproducibility and want your infrastructure to be code</li>
<li>You want to rebuild a machine from scratch in one command and have it come out identical</li>
<li>You enjoy learning fundamentally different approaches to old problems</li>
</ul>
<p>If you just want to run a few containers and don&rsquo;t want to invest in learning a new paradigm, stick with Docker Compose on Ubuntu. That&rsquo;s a completely valid choice.</p>
<p>A word on the popular alternatives. Kubernetes comes up a lot in homelab circles. I have no interest in running it at home. It&rsquo;s a single host, I don&rsquo;t need high availability, and I don&rsquo;t want to spend a Sunday afternoon debugging a pod networking issue when I just want to watch a movie. Proxmox I&rsquo;ve used before and it&rsquo;s genuinely good, but it doesn&rsquo;t solve the idempotency problem. You still end up with VMs and containers that drift over time. Same goes for TrueNAS and similar. I also prefer staying close to the metal. I&rsquo;m comfortable with Linux and I&rsquo;d rather own the whole stack than add an abstraction layer I have to understand on top of it.</p>
<p>But if you&rsquo;re ready to think about your homelab the way a software engineer thinks about code (versioned, tested, reviewable, rollbackable), NixOS is worth every frustrating moment of the learning curve.</p>
<p>See you in <a href="../nixos-part-2/">Part 2</a>, where we&rsquo;ll boot the installer and write our first <code>flake.nix</code>.</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://nixos.org/manual/nixos/stable/">NixOS Manual</a>: the authoritative reference</li>
<li><a href="https://nix.dev">nix.dev</a>: excellent learning-oriented guides</li>
<li><a href="https://zero-to-nix.com">Zero to Nix</a>: a modern introduction to Nix concepts</li>
<li><a href="https://search.nixos.org/packages">Nixpkgs Search</a>: find packages and NixOS options</li>
<li><a href="https://github.com/nix-community/awesome-nix">awesome-nix</a>: curated list of community resources</li>
<li><a href="https://leanpub.com/nixos-in-production">NixOS In Production</a>: The NixOS handbook for professional use</li>
</ul>
<hr>
<p><em>Written in March 2026. NixOS 25.11 (unstable channel at time of writing), Nix 2.24.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
