<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Antoine Moran — Blog</title>
  <subtitle>Notes and write-ups by Antoine Moran.</subtitle>
  <link href="https://amoran.fr/blog/feed.xml" rel="self" />
  <link href="https://amoran.fr/" />
  <updated>2026-10-30T12:00:00Z</updated>
  <id>https://amoran.fr/</id>
  <author>
    <name>Antoine Moran</name>
  </author>
  <entry>
    <title>Organising my NixOS config with den</title>
    <link href="https://amoran.fr/blog/nixos-with-den/" />
    <updated>2026-10-30T12:00:00Z</updated>
    <id>https://amoran.fr/blog/nixos-with-den/</id>
    <content type="html">&lt;p&gt;In my &lt;a href=&quot;https://amoran.fr/blog/why-nixos/&quot;&gt;last post&lt;/a&gt; I explained why I use NixOS on my laptop and my servers. This
one is about how the config itself is organised. It&#39;s managed with
&lt;a href=&quot;https://github.com/denful/den&quot;&gt;den&lt;/a&gt;, and I think it&#39;s a really nice way to structure a NixOS
config once you have more than one machine.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;A common way to organise a NixOS config is one folder per machine, each with its own
&lt;code&gt;configuration.nix&lt;/code&gt; that imports shared modules. That works, but it gets messy when one feature
needs to touch several parts of the system.&lt;/p&gt;
&lt;p&gt;For example, when I add a web service to my server, I need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the service itself, often a container;&lt;/li&gt;
&lt;li&gt;a route in Traefik, my reverse proxy, sometimes restricted to my VPN or behind single sign-on;&lt;/li&gt;
&lt;li&gt;sometimes a firewall rule;&lt;/li&gt;
&lt;li&gt;an uptime check in Gatus, my monitoring tool;&lt;/li&gt;
&lt;li&gt;a tile on my Homepage dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With a config organised by machine, these things end up in different files, and when you remove the
service you have to remember to clean up all of them. With den, everything about the service is in
one place.&lt;/p&gt;
&lt;h2&gt;Aspects&lt;/h2&gt;
&lt;p&gt;den is based on the &amp;quot;dendritic&amp;quot; pattern. Every &lt;code&gt;.nix&lt;/code&gt; file in my &lt;code&gt;modules/&lt;/code&gt; folder is a
&lt;a href=&quot;https://flake.parts/&quot;&gt;flake-parts&lt;/a&gt; module, and &lt;a href=&quot;https://github.com/denful/import-tree&quot;&gt;import-tree&lt;/a&gt;
loads all of them automatically, so I never have to maintain a list of imports. Files whose name
starts with &lt;code&gt;_&lt;/code&gt;, like &lt;code&gt;_hardware.nix&lt;/code&gt;, are skipped.&lt;/p&gt;
&lt;p&gt;On top of that, den adds &lt;strong&gt;aspects&lt;/strong&gt;. An aspect is a named piece of configuration. It can contain
NixOS options, Home Manager options, and a list of other aspects to include. Machines and users are
declared separately, and a machine is basically the list of aspects it uses. This is gila, my
monitoring server, a bit shortened:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hosts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x86_64&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;linux&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gila&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;antoine &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gila&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;includes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shell
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;common
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nix
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sops
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;traefik
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gatus&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;public
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ntfy
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;beszel&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;agent
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My main server has the same kind of list, just longer (59 entries). To see what runs on a machine,
I only need to read that list.&lt;/p&gt;
&lt;h2&gt;Reusable aspects&lt;/h2&gt;
&lt;p&gt;Aspects can also be functions that take arguments. den calls these &amp;quot;batteries&amp;quot;, and you can write
your own under &lt;code&gt;den.provides&lt;/code&gt;, then use them as &lt;code&gt;den._.&amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The one I use the most is called &lt;code&gt;proxied-service&lt;/code&gt;. You give it a subdomain and a port, plus a few
options, and it creates the Traefik route, the VPN restriction if needed, the single sign-on with
Kanidm if needed, the Gatus check and the Homepage entry. With it, my whole Vaultwarden config looks
like this:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultwarden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  includes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;proxied&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;service &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      subdomain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      vpnOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      monitorName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vaultwarden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      homepage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;group &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Small Icons&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  nixos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    sops&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultwarden_token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* … */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultwarden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      enable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      environmentFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sops&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultwarden_token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ROCKET_PORT &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;41 of my aspects use &lt;code&gt;proxied-service&lt;/code&gt;. I also have one for containers, &lt;code&gt;media-container&lt;/code&gt;, which
sets the usual user and timezone variables, the shared Podman network and the secrets file. If I
want to change how all my services are exposed, I only change one file.&lt;/p&gt;
&lt;h2&gt;Aspects that send data to each other&lt;/h2&gt;
&lt;p&gt;You might have noticed that the Vaultwarden aspect never mentions Gatus or Homepage, but it still
appears in both. This works with what den calls &lt;strong&gt;quirks&lt;/strong&gt;. A quirk is like a named list that any
aspect can add to, and that another aspect can read. I declare mine once:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quirks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gatus&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gatus monitoring endpoint contributions from service aspects.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quirks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;homepage&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Homepage dashboard entry contributions from service aspects.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quirks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firewall&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Firewall port contributions from service aspects.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A service adds an entry (here through &lt;code&gt;proxied-service&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;gatus&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vaultwarden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://127.0.0.1:8222&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  conditions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[STATUS] == 200&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  alerts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ntfy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the Gatus aspect receives every entry from the machine as an argument:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nixos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;gatus&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;endpoints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gatus&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The firewall works the same way. My WireGuard aspect adds &lt;code&gt;firewall-port.udp = [5553];&lt;/code&gt;, and the
server opens all the ports its aspects asked for:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;allowedUDPPorts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concatMap &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;udp &lt;span class=&quot;token keyword&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; firewall&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So my dashboard, my monitoring and my open ports always match what is actually installed. When I
remove a service from the list, its tile, its check and its port go away with it.&lt;/p&gt;
&lt;h3&gt;Between machines&lt;/h3&gt;
&lt;p&gt;With den&#39;s &lt;strong&gt;policies&lt;/strong&gt;, this also works across machines. Each machine declares its own WireGuard
peer in its own file:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gila&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wireguard&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;peer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  publicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;…&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  allowedIPs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;192.168.100.4/32&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the WireGuard server collects the peers of all my machines:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;policies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fleet&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;wireguard&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;peers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;policy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;from &lt;span class=&quot;token string&quot;&gt;&quot;wireguard-peer&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;policy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pipe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;collect &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;host&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; host &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add a new machine to the VPN, I declare its key in its own config, and I don&#39;t need to edit the
server&#39;s list of peers.&lt;/p&gt;
&lt;h2&gt;Impermanence&lt;/h2&gt;
&lt;p&gt;As I said in the last post, my laptop erases its root filesystem on every boot, so every folder
that needs to be kept has to be declared. With den, each aspect declares its own folders. For
Firefox:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firefox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;includes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persist &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;home&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.mozilla&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;impermanence&lt;/code&gt; aspect then collects everything and passes it to the impermanence module. If I
remove Firefox, &lt;code&gt;.mozilla&lt;/code&gt; isn&#39;t kept anymore.&lt;/p&gt;
&lt;h2&gt;NixOS and Home Manager together&lt;/h2&gt;
&lt;p&gt;In a plain NixOS config, system settings and Home Manager settings live in two separate module
systems. If one feature needs both, you write two modules and import them in two different places:
one in the machine&#39;s config, the other in &lt;code&gt;home-manager.users.&amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The dendritic pattern on its own already lets you put both in the same file, as
&lt;code&gt;flake.modules.nixos.foo&lt;/code&gt; and &lt;code&gt;flake.modules.homeManager.foo&lt;/code&gt;, but you still have to import each
one in the right place yourself.&lt;/p&gt;
&lt;p&gt;With den, an aspect can have a &lt;code&gt;nixos&lt;/code&gt; part and a &lt;code&gt;homeManager&lt;/code&gt; part, and including the aspect once
is enough: den applies the NixOS part to the machine and the Home Manager part to its users. My
impermanence aspect does both:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;impermanence &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  nixos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;persist&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lib&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    environment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persistence&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/persist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
      lib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concatMap &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nixos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directories &lt;span class=&quot;token keyword&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; persist&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  homeManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;persist&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lib&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    home&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persistence&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/persist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
      lib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concatMap &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;home&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directories &lt;span class=&quot;token keyword&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; persist&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Machines and users&lt;/h2&gt;
&lt;p&gt;den makes a difference between a machine, a user, and a user on a specific machine. On my laptop,
the desktop is configured for my user on that machine, under &lt;code&gt;_.antoine&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;balrog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;antoine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;includes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;niri
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dms
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firefox
  den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;theme
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An aspect can also ask which user it&#39;s being applied to. My Git config takes my name and email from
my user declaration instead of writing them again:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;den&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aspects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  homeManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;programs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flake inputs&lt;/h2&gt;
&lt;p&gt;The last tool I use is &lt;a href=&quot;https://github.com/denful/flake-file&quot;&gt;flake-file&lt;/a&gt;. Instead of writing all
the inputs in &lt;code&gt;flake.nix&lt;/code&gt;, each module declares the inputs it needs, and &lt;code&gt;flake.nix&lt;/code&gt; is generated
with &lt;code&gt;nix run .#write-flake&lt;/code&gt;. For example, my impermanence aspect starts with:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;flake&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inputs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;impermanence&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;github:nix-community/impermanence&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I delete the aspect, the input goes away too.&lt;/p&gt;
&lt;h2&gt;The downsides&lt;/h2&gt;
&lt;p&gt;There&#39;s more to learn than with a classic config: aspects, batteries, quirks and policies come on
top of the NixOS module system, and when something goes wrong the errors can be hard to follow.&lt;/p&gt;
&lt;p&gt;den also looks at the names of a function&#39;s arguments (&lt;code&gt;host&lt;/code&gt;, &lt;code&gt;user&lt;/code&gt;…) to decide where to apply
it. So replacing &lt;code&gt;{host, ...}&lt;/code&gt; with &lt;code&gt;_&lt;/code&gt; changes what the code does, even though linters like
deadnix suggest doing exactly that when the argument isn&#39;t used.&lt;/p&gt;
&lt;p&gt;Finally, den is still young and changes quite a lot.&lt;/p&gt;
&lt;h2&gt;In summary&lt;/h2&gt;
&lt;p&gt;For a single machine, a normal config is probably enough. But with a server running many services
and several machines that share things, den makes it much easier to add or remove something
without forgetting a piece somewhere. If you want to see a complete example, the author of den
shares his own config, &lt;a href=&quot;https://tangled.org/oeiuwq.com/vix&quot;&gt;vix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Why NixOS stopped my distro hopping</title>
    <link href="https://amoran.fr/blog/why-nixos/" />
    <updated>2026-10-30T00:00:00Z</updated>
    <id>https://amoran.fr/blog/why-nixos/</id>
    <content type="html">&lt;p&gt;I&#39;ve been using &lt;a href=&quot;https://nixos.org/&quot;&gt;NixOS&lt;/a&gt; for a while now, on my laptop and on the servers I
self-host my stuff on. This post explains how I use it and why I think it&#39;s the best setup I&#39;ve
had so far.&lt;/p&gt;
&lt;p&gt;Before NixOS I used Arch. I liked it, but I got tired of my system slowly drifting: packages
I&#39;d installed once and forgotten, config files I&#39;d edited by hand, leftovers from things I&#39;d
tried. After a while it was full of garbage and I couldn&#39;t tell anymore what I had changed. With
NixOS that doesn&#39;t happen, so I haven&#39;t had a reason to switch since.&lt;/p&gt;
&lt;h2&gt;My setup&lt;/h2&gt;
&lt;p&gt;I have three machines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;balrog&lt;/strong&gt;, my laptop, running &lt;a href=&quot;https://github.com/YaLTeR/niri&quot;&gt;Niri&lt;/a&gt; with
&lt;a href=&quot;https://danklinux.com/&quot;&gt;DankMaterialShell&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cyboogie&lt;/strong&gt;, a server that hosts most of what I use day to day: photos with Immich, passwords
with Vaultwarden, mail, music, documents with Paperless, my own Git forge, and a lot more.
Around fifty services in total.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gila&lt;/strong&gt;, a small VPS that monitors cyboogie and sends me a notification when something is down.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three are configured from the same Git repository.&lt;/p&gt;
&lt;h2&gt;Configuring everything in one place&lt;/h2&gt;
&lt;p&gt;On most distros you install the system, then install packages and edit config files as you go.
After a few years you end up with a machine where nobody really knows what was changed and why.&lt;/p&gt;
&lt;p&gt;On NixOS you don&#39;t do that. The whole system is described in Nix files, and you apply them with
&lt;code&gt;nixos-rebuild switch&lt;/code&gt;. If I want Vaultwarden on my server, I add this to the config:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultwarden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  enable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ROCKET_PORT &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and rebuild. If I remove it, it&#39;s gone from the system on the next rebuild. Nothing is left behind.&lt;/p&gt;
&lt;p&gt;Since the config is in Git, I also get the history of every change, and I can look up how
anything is configured with a simple search in the repo, instead of looking around in &lt;code&gt;/etc&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Rolling back&lt;/h2&gt;
&lt;p&gt;Each rebuild creates a new &amp;quot;generation&amp;quot;, and the previous ones are kept. If an update breaks
something, I can choose an older generation in the boot menu, or run
&lt;code&gt;nixos-rebuild switch --rollback&lt;/code&gt;, and I&#39;m back to a working system.&lt;/p&gt;
&lt;h2&gt;Deploying from my laptop&lt;/h2&gt;
&lt;p&gt;I don&#39;t need to log into the servers to update them. Since all the configs are in the same repo,
I can build any machine&#39;s system from my laptop and send it over SSH:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;nixos-rebuild switch &lt;span class=&quot;token parameter variable&quot;&gt;--flake&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;#gila --target-host gila --sudo&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is great for gila. It&#39;s a small VPS that would take ages to compile anything, so my laptop
does the building and gila just receives the result and switches to it.&lt;/p&gt;
&lt;h2&gt;Erasing the system on every boot&lt;/h2&gt;
&lt;p&gt;Because the system is rebuilt from the config anyway, you can go one step further. On my laptop,
&lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt; are reset to an empty state every time it boots. Only the folders I list in my
config are kept, using the &lt;a href=&quot;https://github.com/nix-community/impermanence&quot;&gt;impermanence&lt;/a&gt; module:&lt;/p&gt;
&lt;pre class=&quot;language-nix&quot;&gt;&lt;code class=&quot;language-nix&quot;&gt;home&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;Documents&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;Downloads&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;.ssh&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;.local/share/fish&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like this a lot. Programs can&#39;t leave files around my home folder forever, and the list of
persisted folders is also the list of what I need to back up.&lt;/p&gt;
&lt;h2&gt;Running a server with fifty services&lt;/h2&gt;
&lt;p&gt;This is where NixOS helps me the most. Maintaining that many services alone would be a lot of
work, but most of it is automated now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every input of my config is pinned in &lt;code&gt;flake.lock&lt;/code&gt;, and every container image is pinned to a
specific version and digest, so nothing changes unless I update it.&lt;/li&gt;
&lt;li&gt;Every week, a job on my Forgejo instance updates &lt;code&gt;flake.lock&lt;/code&gt; and opens a pull request.
&lt;a href=&quot;https://docs.renovatebot.com/&quot;&gt;Renovate&lt;/a&gt; does the same for container images, and merges minor
updates automatically.&lt;/li&gt;
&lt;li&gt;On every pull request, CI builds the configuration of all three machines. If an update breaks
something, I see it there before it reaches the server.&lt;/li&gt;
&lt;li&gt;The builds go into my own binary cache, served by
&lt;a href=&quot;https://github.com/nix-community/harmonia&quot;&gt;Harmonia&lt;/a&gt;, so my laptop downloads what CI already
built instead of compiling it again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Secrets&lt;/h2&gt;
&lt;p&gt;Passwords and API keys are in the same repository, encrypted with
&lt;a href=&quot;https://github.com/Mic92/sops-nix&quot;&gt;sops-nix&lt;/a&gt;. Each machine decrypts them with its own SSH host
key, and only gets the secrets its config uses. When I set up a machine, I don&#39;t have to copy
secret files around by hand.&lt;/p&gt;
&lt;h2&gt;The downsides&lt;/h2&gt;
&lt;p&gt;NixOS isn&#39;t easy to learn. You need to learn the Nix language, which is unusual, and the
documentation is spread between the manual, the &lt;a href=&quot;https://wiki.nixos.org/&quot;&gt;wiki&lt;/a&gt;, forum posts and
other people&#39;s configs. Error messages can be hard to read.&lt;/p&gt;
&lt;p&gt;Software that comes as a prebuilt binary can also be a problem, because it expects libraries in
places that don&#39;t exist on NixOS. I use &lt;code&gt;nix-ld&lt;/code&gt; on my laptop for that, and it works most of the
time.&lt;/p&gt;
&lt;h2&gt;In summary&lt;/h2&gt;
&lt;p&gt;For me it&#39;s been worth it. I can rebuild any of my machines from scratch, undo a bad change, and
always know what&#39;s running where. If you&#39;re curious, reading other people&#39;s configs is a good way
to start. There are a lot of them on GitHub.&lt;/p&gt;
&lt;p&gt;In the next post I&#39;ll explain how I organise my config with &lt;a href=&quot;https://github.com/denful/den&quot;&gt;den&lt;/a&gt;,
which made a big difference once the server got bigger.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content>
  </entry>
</feed>