<?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 Carlos Vaz</title>
    <link>https://carlosvaz.com/tags/nixos/</link>
    <description>Recent content in Nixos on Carlos Vaz</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-US</language>
    <managingEditor>carlos@carjorvaz.com (Carlos Vaz)</managingEditor>
    <webMaster>carlos@carjorvaz.com (Carlos Vaz)</webMaster>
    <lastBuildDate>Mon, 21 Apr 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://carlosvaz.com/tags/nixos/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Setting up Samba shares on NixOS (with support for macOS Time Machine backups)</title>
      <link>https://carlosvaz.com/posts/setting-up-samba-shares-on-nixos-with-support-for-macos-time-machine-backups/</link>
      <pubDate>Mon, 21 Apr 2025 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/setting-up-samba-shares-on-nixos-with-support-for-macos-time-machine-backups/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been wanting to set up proper backups for my Mac for quite some time now.&#xA;Not only that, I also would like to take advantage of the storage space I have available on my home server to store and archive miscellaneous files, in a way that&amp;rsquo;s easily accessible from my Mac, properly integrated within finder but without having a copy of every file locally (as I would with Nextcloud sync, for example).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve been wanting to set up proper backups for my Mac for quite some time now.
Not only that, I also would like to take advantage of the storage space I have available on my home server to store and archive miscellaneous files, in a way that&rsquo;s easily accessible from my Mac, properly integrated within finder but without having a copy of every file locally (as I would with Nextcloud sync, for example).</p>
<p>For backups, I was considering setting up borgmatic with its home-manager integration, so that it would also work on my Linux desktops.</p>
<p>But I later found that you can use macOS&rsquo;s native backups mechanism &ldquo;Time Machine&rdquo; with remote Samba shares, so I looked further into that, to have a better and more nicely integrated experience.
By setting up Samba on my home server, I could make a share for my time machine backups and another share for miscelleanous files, integrated within Finder.</p>
<p>So here&rsquo;s how I did it.</p>
<h2 id="setting-up-the-server">Setting up the server</h2>
<p>Following the NixOS wiki page, here&rsquo;s a basic setup for the Samba server:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>services <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  samba <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    settings <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      global <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;workgroup&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WORKGROUP&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;server string&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;smbnix&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;netbios name&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;smbnix&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;security&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;user&#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:#e6db74">&#34;my_share&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;path&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/home/john&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;valid users&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;john&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;force user&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;john&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;public&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;no&#34;</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;writeable&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#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>
</span></span><span style="display:flex;"><span>  samba-wsdd <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    discovery <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  avahi <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    publish<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    publish<span style="color:#f92672">.</span>userServices <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    nssmdns4 <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><p>With this, you should now be able to access the on Samba clients at <code>smb://&lt;ip_address&gt;/my_share</code>.
And the nice part is that Gnome Files, macOS Finder, and other applications can use this directly.</p>
<p>And here&rsquo;s my own setup, where I also add some options for better macOS compatibility, and declaratively set ownership of the shares&rsquo; directories on the server system:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span><span style="color:#66d9ef">let</span>
</span></span><span style="display:flex;"><span>  user <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;samba&#34;</span>;
</span></span><span style="display:flex;"><span>  privatePath <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/mnt/samba/private&#34;</span>;
</span></span><span style="display:flex;"><span>  tmPath <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/mnt/samba/tm_share&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">in</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># https://wiki.nixos.org/wiki/Samba#Server_setup</span>
</span></span><span style="display:flex;"><span>  services <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    samba <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      settings <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        global <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;workgroup&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WORKGROUP&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;server string&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;smbnix&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;netbios name&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;smbnix&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;security&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;user&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#75715e"># Only available on localhost and Tailscale</span>
</span></span><span style="display:flex;"><span>          <span style="color:#75715e"># note: localhost is the ipv6 localhost ::1</span>
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;hosts allow&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;100.64.0.0/10 127.0.0.1 localhost&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;hosts deny&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;0.0.0.0/0&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;guest account&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nobody&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;map to guest&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;bad user&#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:#e6db74">&#34;private&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;path&#34;</span> <span style="color:#f92672">=</span> privatePath;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;valid users&#34;</span> <span style="color:#f92672">=</span> user;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;public&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;no&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;writeable&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;force user&#34;</span> <span style="color:#f92672">=</span> user;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;fruit:aapl&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;vfs objects&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;catia fruit streams_xattr&#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:#e6db74">&#34;tm_share&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;path&#34;</span> <span style="color:#f92672">=</span> tmPath;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;valid users&#34;</span> <span style="color:#f92672">=</span> user;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;public&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;no&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;writeable&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;force user&#34;</span> <span style="color:#f92672">=</span> user;
</span></span><span style="display:flex;"><span>          <span style="color:#75715e"># Below are the most imporant for macOS compatibility</span>
</span></span><span style="display:flex;"><span>          <span style="color:#75715e"># Change the above to suit your needs</span>
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;fruit:aapl&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;fruit:time machine&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;vfs objects&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;catia fruit streams_xattr&#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>    samba-wsdd <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      discovery <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    avahi <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      publish<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      publish<span style="color:#f92672">.</span>userServices <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      nssmdns4 <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># https://wiki.nixos.org/wiki/Samba#Apple_Time_Machine</span>
</span></span><span style="display:flex;"><span>      extraServiceFiles <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        timemachine <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          &lt;?xml version=&#34;1.0&#34; standalone=&#39;no&#39;?&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          &lt;!DOCTYPE service-group SYSTEM &#34;avahi-service.dtd&#34;&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          &lt;service-group&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &lt;name replace-wildcards=&#34;yes&#34;&gt;%h&lt;/name&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &lt;service&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;type&gt;_smb._tcp&lt;/type&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;port&gt;445&lt;/port&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &lt;/service&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;service&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;type&gt;_device-info._tcp&lt;/type&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;port&gt;0&lt;/port&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;txt-record&gt;model=TimeCapsule8,119&lt;/txt-record&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &lt;/service&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &lt;service&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;type&gt;_adisk._tcp&lt;/type&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;!--
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                change tm_share to share name, if you changed it.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              --&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;txt-record&gt;dk0=adVN=tm_share,adVF=0x82&lt;/txt-record&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              &lt;txt-record&gt;sys=waMa=0,adVF=0x100&lt;/txt-record&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &lt;/service&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          &lt;/service-group&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        &#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><span style="display:flex;"><span>  fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>privatePath<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zsafe/samba&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zfs&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfsutil&#34;</span> ];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>tmPath<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zsafe/timemachine&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zfs&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfsutil&#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:#75715e"># Set up password: https://wiki.nixos.org/wiki/Samba#User_Authentication</span>
</span></span><span style="display:flex;"><span>  users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span><span style="color:#e6db74">${</span>user<span style="color:#e6db74">}</span><span style="color:#f92672">.</span>isNormalUser <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Share path must be owned by the respective unix user. (e.g. ❯ chown -R samba: /samba)</span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#f92672">.</span>tmpfiles<span style="color:#f92672">.</span>rules <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;d </span><span style="color:#e6db74">${</span>privatePath<span style="color:#e6db74">}</span><span style="color:#e6db74"> 0755 </span><span style="color:#e6db74">${</span>user<span style="color:#e6db74">}</span><span style="color:#e6db74"> users&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;d </span><span style="color:#e6db74">${</span>tmPath<span style="color:#e6db74">}</span><span style="color:#e6db74"> 0755 </span><span style="color:#e6db74">${</span>user<span style="color:#e6db74">}</span><span style="color:#e6db74"> users&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>In my setup, I also make it so that only devices in my Tailscale network can access the shares.
Unfortunately, I couldn&rsquo;t seem to connect to Samba shares using Tailscale&rsquo;s MagicDNS, so I had to fallback to using the server&rsquo;s Tailscale IP instead.</p>
<p>I also created a separate user just for this, as I would prefer not to connect directly as root.
Something that wasn&rsquo;t immediately obvious is that each Samba user needs to have a corresponding unix user on the host system and also that the directories must be accessible by that user.
So I also added systemd-tmpfiles rules to do that automatically.</p>
<p>To set the samba user&rsquo;s password: <code>sudo smbpasswd -a samba</code></p>
<p>This version also has a separate share just for Time Machine backups.
To set that up, first connect through Finder &gt; Go &gt; Connect to Server (CMD + K) &gt; <code>smb://&lt;ip_address&gt;/tm_share</code> and connect with the Samba user and its samba password.</p>
<p>Finally, this should now show up as an available backup disk in Time Machine settings.
Here, I set up a more or less arbitrary quota of 1TB (which you can&rsquo;t seem to easily change later) and also enabled encryption.
After that, Time Machine should be set up and you can now use the fancy interface to show the contents of a directory in the past and restore files (and also just fully restore on freshly set up Mac).</p>
<p>Some little tricks that weren&rsquo;t immediately obvious were how to pin a Samba folder to Finder&rsquo;s sidebar and Time Machine complaining that it lost access to the share (because I became offline and/or suspended the computer).</p>
<p>To pin the folder to Finder&rsquo;s sidebar, open a terminal and do <code>open /Volumes/&lt;share_name&gt;</code> after mounting it through Finder as described above.
Now, on Finder &gt; View &gt; Show Path Bar (CMD + OPT + P) and drag the icon next to the share&rsquo;s name to the Sidebar.</p>
<p>After becoming offline, accessing (and therefore mounting) the share didnt&rsquo; seem to be enough.
I still quite haven&rsquo;t figured out how to recover reliably, but manually opening the share and mounting/opening its Sparse Bundle file seems to allow Time Machine to be able to see it again.</p>
<h2 id="closing-remarks">Closing remarks</h2>
<p>I&rsquo;m happy that after some configuration and troubleshooting, all appears to be working smoothly.</p>
<p>It seems that time machine backups are basically APFS snapshots, which themselves seem to be analagous to ZFS snapshots.
So I&rsquo;m happy that, in a way, my setup is similar to having automatic snapshots on my Mac and then sending them encrypted to some remote server, as I&rsquo;ve been meaning to do on my remaining NixOS servers with a Sanoid/Syncoid setup.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://wiki.nixos.org/wiki/Samba">https://wiki.nixos.org/wiki/Samba</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>iPXE booting with NixOS</title>
      <link>https://carlosvaz.com/posts/ipxe-booting-with-nixos/</link>
      <pubDate>Mon, 07 Aug 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/ipxe-booting-with-nixos/</guid>
      <description>&lt;p&gt;When you only manage a few systems, it is usually enough to install NixOS on them using a live installer on a USB drive.&#xA;When dealing with almost a hundred systems, however, using USB drives becomes unfeasable and solutions like PXE booting become necessary.&lt;/p&gt;&#xA;&lt;p&gt;Thankfully, Pixiecore makes it easy to set up an iPXE server on NixOS, a task that could otherwise be quite complex.&lt;/p&gt;&#xA;&lt;p&gt;The simplest iPXE server we can set up on NixOS can look like the following:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>When you only manage a few systems, it is usually enough to install NixOS on them using a live installer on a USB drive.
When dealing with almost a hundred systems, however, using USB drives becomes unfeasable and solutions like PXE booting become necessary.</p>
<p>Thankfully, Pixiecore makes it easy to set up an iPXE server on NixOS, a task that could otherwise be quite complex.</p>
<p>The simplest iPXE server we can set up on NixOS can look like the following:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</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:#f92672">.</span>pixiecore <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    dhcpNoBind <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    kernel <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://boot.netboot.xyz&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>In this configuration, we enable the Pixiecore service, set it to work along the DHCP server that already exists on our network and to boot the <a href="https://netboot.xyz">netboot.xyz</a> iPXE image, a common option which gives you a selection of live installers on boot.</p>
<p>This may be enough for your environment but, as we&rsquo;re running NixOS, we&rsquo;d like to an image customized to our needs, having SSH access pre-configured, for example:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> inputs<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span>
</span></span><span style="display:flex;"><span>  sys <span style="color:#f92672">=</span> inputs<span style="color:#f92672">.</span>nixos<span style="color:#f92672">.</span>lib<span style="color:#f92672">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>    system <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>    modules <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>      ({ config<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> modulesPath<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }: {
</span></span><span style="display:flex;"><span>        imports <span style="color:#f92672">=</span> [ (modulesPath <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;/installer/netboot/netboot-minimal.nix&#34;</span>) ];
</span></span><span style="display:flex;"><span>        config <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>          services<span style="color:#f92672">.</span>openssh <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>            enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>            openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            settings <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>              PasswordAuthentication <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>              KbdInteractiveAuthentication <span style="color:#f92672">=</span> <span style="color:#66d9ef">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>          users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span>root<span style="color:#f92672">.</span>openssh<span style="color:#f92672">.</span>authorizedKeys<span style="color:#f92672">.</span>keys <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...&#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>  build <span style="color:#f92672">=</span> sys<span style="color:#f92672">.</span>config<span style="color:#f92672">.</span>system<span style="color:#f92672">.</span>build;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">in</span> {
</span></span><span style="display:flex;"><span>  services<span style="color:#f92672">.</span>pixiecore <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    dhcpNoBind <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>; <span style="color:#75715e"># Use existing DHCP server.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    mode <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;boot&#34;</span>;
</span></span><span style="display:flex;"><span>    kernel <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>build<span style="color:#f92672">.</span>kernel<span style="color:#e6db74">}</span><span style="color:#e6db74">/bzImage&#34;</span>;
</span></span><span style="display:flex;"><span>    initrd <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>build<span style="color:#f92672">.</span>netbootRamdisk<span style="color:#e6db74">}</span><span style="color:#e6db74">/initrd&#34;</span>;
</span></span><span style="display:flex;"><span>    cmdLine <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;init=</span><span style="color:#e6db74">${</span>build<span style="color:#f92672">.</span>toplevel<span style="color:#e6db74">}</span><span style="color:#e6db74">/init loglevel=4&#34;</span>;
</span></span><span style="display:flex;"><span>    debug <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>The previous example does exactly that.</p>
<p>This now becomes very powerful and we&rsquo;ve just starting using this on the lab computers we manage.
We set the boot order to boot first from the hard disk and, if it&rsquo;s not bootable, it will automatically boot from iPXE, still allowing remote access from the administrators and allowing us, if necessary, to redeploy its configuration with <a href="https://github.com/numtide/nixos-anywhere">nixos-anywhere</a>, as I&rsquo;ll explain in a future blog post.</p>
<p>But we also want to be able to trigger an iPXE boot remotely, even if the current hard disk has a bootable configuration.</p>
<p>Initially, it wasn&rsquo;t very obvious how we should be able to do this.
One possibility would be to use <code>efibootmgr</code> to change the boot order accordingly, but it would make it more difficult to return to booting from local hard disk after the redeploy.</p>
<p>After some reflection, we settled on creating a special GRUB entry that boots from iPXE.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</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:#f92672">.</span>loader <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    efi<span style="color:#f92672">.</span>canTouchEfiVariables <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    grub <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      efiSupport <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nodev&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      extraFiles <span style="color:#f92672">=</span> { <span style="color:#e6db74">&#34;ipxe.efi&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>ipxe<span style="color:#e6db74">}</span><span style="color:#e6db74">/ipxe.efi&#34;</span>; };
</span></span><span style="display:flex;"><span>      extraEntries <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        menuentry &#34;Reinstall via iPXE&#34; {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          chainloader /ipxe.efi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      &#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>Presumably, this only works on UEFI systems.</p>
<p>Finding out how to create this entry wasn&rsquo;t trivial at all, I couldn&rsquo;t find anyone else doing it this way,  but it seems to work perfectly fine.</p>
<p>Having this boot entry on its own isn&rsquo;t much different from choosing a different one-time boot option, so I&rsquo;ll look into using the <code>grub-reboot</code> tool to be able to trigger it remotely, although I haven&rsquo;t tested that yet on NixOS.</p>
<p><em>2023/08/24 Update</em>: I can confirm that <code>grub-reboot</code> works as expected on NixOS.</p>
<p>For example, you can get the available entries on your system like so:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">[root@nixos:~]# grep menuentry /boot/grub/grub.cfg
menuentry &#34;NixOS - Default&#34; --class nixos {
menuentry &#34;Windows 10&#34; {
menuentry &#34;iPXE Boot&#34; {
menuentry &#34;NixOS - Configuration 19 (2023-08-24 - 23.05.20230820.475d5ae)&#34; --class nixos {</code></pre><p>From this output, we&rsquo;d run <code>grub-reboot 2</code> to boot to iPXE on next reboot.</p>
<p>There&rsquo;s also a more complicated approach, where the iPXE boot entry won&rsquo;t be visible by default, only when you trigger it remotely.</p>
<p>To do this, we use the following snippet instead:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</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:#f92672">.</span>shellAliases<span style="color:#f92672">.</span>reboot2PXE <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>grub2<span style="color:#e6db74">}</span><span style="color:#e6db74">/bin/grub-editenv /boot/grub/grubenv set entry=ipxe &amp;&amp; reboot&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#f92672">.</span>loader <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    efi<span style="color:#f92672">.</span>canTouchEfiVariables <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    grub <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      efiSupport <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nodev&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      extraFiles <span style="color:#f92672">=</span> { <span style="color:#e6db74">&#34;ipxe.efi&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>ipxe<span style="color:#e6db74">}</span><span style="color:#e6db74">/ipxe.efi&#34;</span>; };
</span></span><span style="display:flex;"><span>      extraConfig <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        if [ &#34;</span><span style="color:#ae81ff">&#39;&#39;$</span><span style="color:#e6db74">{entry}&#34; = &#34;ipxe&#34; ]; then
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          set entry=&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          save_env --file /grub/grubenv entry
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          menuentry &#34;Reinstall via iPXE&#34; {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            chainloader /ipxe.efi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        fi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      &#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>Now, if we call <code>reboot2PXE</code>, the system will immediately reboot to iPXE, while not showing the iPXE boot entry on regular boots.</p>
<h2 id="closing-remarks">Closing remarks</h2>
<p>As you can see by these snippets, setting up an iPXE server doesn&rsquo;t have to be a chore.</p>
<p>As basically every computer built in the last 10 years has proper iPXE booting support, when managing tens of bare metal machines, setups like these drastically make the sysadmins&rsquo; lives easier.</p>
<p>Although I believe it would still be technically possible to remotely configure and recover these systems using lower level tools like AMT, I feel this would create a more complicated setup.
I&rsquo;m still looking to experiment with <a href="https://meshcentral.com">MeshCentral</a> to explore these lower level AMT capibilities, and that may become a future blog post.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/danderson/netboot/tree/main/pixiecore">https://github.com/danderson/netboot/tree/main/pixiecore</a></li>
<li><a href="https://nixos.wiki/wiki/Netboot">https://nixos.wiki/wiki/Netboot</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Rootless Podman and Docker-Compose on NixOS</title>
      <link>https://carlosvaz.com/posts/rootless-podman-and-docker-compose-on-nixos/</link>
      <pubDate>Sun, 06 Aug 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/rootless-podman-and-docker-compose-on-nixos/</guid>
      <description>&lt;p&gt;At my university&amp;rsquo;s Computer Science and Engineering department, we want to allow students to use containers in the lab computers while not giving them root priviliges, as running Docker usually requires.&lt;/p&gt;&#xA;&lt;p&gt;In the past, on our Ubuntu systems, we had a setup of rootless-Docker I&amp;rsquo;m not sure ever worked properly.&#xA;There also seems to exist an option on NixOS to enable rootless-Docker, but we also had some issues in using it.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>At my university&rsquo;s Computer Science and Engineering department, we want to allow students to use containers in the lab computers while not giving them root priviliges, as running Docker usually requires.</p>
<p>In the past, on our Ubuntu systems, we had a setup of rootless-Docker I&rsquo;m not sure ever worked properly.
There also seems to exist an option on NixOS to enable rootless-Docker, but we also had some issues in using it.</p>
<p>So we went with Podman, a Docker alternative that&rsquo;s more ready to be run rootless by design.
Also, Docker can just be aliased to Podman and the experience is so identical that the students may not even notice that they&rsquo;re not running Docker proper.</p>
<p>Enabling Podman on NixOS is quite trivial but having it work in a rootless environment requires more configuration:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  virtualisation <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    containers<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    containers<span style="color:#f92672">.</span>storage<span style="color:#f92672">.</span>settings <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      storage <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        driver <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;overlay&#34;</span>;
</span></span><span style="display:flex;"><span>        runroot <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/run/containers/storage&#34;</span>;
</span></span><span style="display:flex;"><span>        graphroot <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/var/lib/containers/storage&#34;</span>;
</span></span><span style="display:flex;"><span>        rootless_storage_path <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/tmp/containers-$USER&#34;</span>;
</span></span><span style="display:flex;"><span>        options<span style="color:#f92672">.</span>overlay<span style="color:#f92672">.</span>mountopt <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nodev,metacopy=on&#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>    oci-containers<span style="color:#f92672">.</span>backend <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;podman&#34;</span>;
</span></span><span style="display:flex;"><span>    podman <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      enableNvidia <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      dockerCompat <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># extraPackages = [ pkgs.zfs ]; # Required if the host is running ZFS</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>  environment<span style="color:#f92672">.</span>systemPackages <span style="color:#f92672">=</span> <span style="color:#66d9ef">with</span> pkgs; [ docker-compose ];
</span></span><span style="display:flex;"><span>  environment<span style="color:#f92672">.</span>extraInit <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    if [ -z &#34;$DOCKER_HOST&#34; -a -n &#34;$XDG_RUNTIME_DIR&#34; ]; then
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      export DOCKER_HOST=&#34;unix://$XDG_RUNTIME_DIR/podman/podman.sock&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    fi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>We set the <code>DOCKER_HOST</code> environment variable on user login so that <code>docker-compose</code> works using the rootless Podman socket.</p>
<p>Because students log into our lab computers using Kerberos, they don&rsquo;t have <code>/etc/subuid</code> and <code>/etc/subgid</code> entries automatically created for them.
So, in this particular setup, we also have a <code>pam_exec</code> hook to create the entries for them, otherwise Podman won&rsquo;t work.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># subidappend is a script we wrote that adds subuid and subgid entries on user login</span>
</span></span><span style="display:flex;"><span>  security<span style="color:#f92672">.</span>pam<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>login<span style="color:#f92672">.</span>text <span style="color:#f92672">=</span> lib<span style="color:#f92672">.</span>mkDefault (lib<span style="color:#f92672">.</span>mkAfter <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    session optional pam_exec.so </span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>subidappend<span style="color:#e6db74">}</span><span style="color:#e6db74">/bin/subidappend
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  &#39;&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  security<span style="color:#f92672">.</span>pam<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>sshd<span style="color:#f92672">.</span>text <span style="color:#f92672">=</span> lib<span style="color:#f92672">.</span>mkDefault (lib<span style="color:#f92672">.</span>mkAfter <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    session optional pam_exec.so </span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>subidappend<span style="color:#e6db74">}</span><span style="color:#e6db74">/bin/subidappend
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  &#39;&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Clean subuids and gids on boot</span>
</span></span><span style="display:flex;"><span>  systemd<span style="color:#f92672">.</span>tmpfiles<span style="color:#f92672">.</span>rules <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>    [ <span style="color:#e6db74">&#34;f+  /etc/subuid 0644 root root -&#34;</span> <span style="color:#e6db74">&#34;f+  /etc/subgid 0644 root root -&#34;</span> ];
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><h2 id="closing-remarks">Closing remarks</h2>
<p>We find this setup to be quite robust, it tends to just work with most Docker workflows, with the execption of using ports under 1024.</p>
<p>This allows students to run almost everything they wish to run on the lab computers, while still not having root privileges.
The Nix package manager also helps with this.
More importantly, students can now easily run services like Nginx and PostgreSQL, which are not trivial at all to run on the host system without root.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md">https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md</a></li>
<li><a href="https://major.io/p/rootless-container-management-with-docker-compose-and-podman/">https://major.io/p/rootless-container-management-with-docker-compose-and-podman/</a></li>
<li><a href="https://nixos.wiki/wiki/Podman">https://nixos.wiki/wiki/Podman</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>The Holy Grail Nextcloud setup made easy by NixOS</title>
      <link>https://carlosvaz.com/posts/the-holy-grail-nextcloud-setup-made-easy-by-nixos/</link>
      <pubDate>Sat, 05 Aug 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/the-holy-grail-nextcloud-setup-made-easy-by-nixos/</guid>
      <description>&lt;p&gt;Nextcloud really is the central piece to most people&amp;rsquo;s self-hosted infrastructure.&lt;/p&gt;&#xA;&lt;p&gt;Providing file synchronization, a web interface to navigate through them, calendar, contacts, tasks, kanban and webmail, it presents itself as a complete GSuite self-hosted alternative.&lt;/p&gt;&#xA;&lt;p&gt;Hosting Nextcloud has become easier over time, thanks to its docker-compose example setups and to the Snap for use mostly on Ubuntu systems. However, having a faster and more optimized setup can take some effort on these platforms. Thankfully, on NixOS it&amp;rsquo;s not hard at all, as I&amp;rsquo;ll show you.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Nextcloud really is the central piece to most people&rsquo;s self-hosted infrastructure.</p>
<p>Providing file synchronization, a web interface to navigate through them, calendar, contacts, tasks, kanban and webmail, it presents itself as a complete GSuite self-hosted alternative.</p>
<p>Hosting Nextcloud has become easier over time, thanks to its docker-compose example setups and to the Snap for use mostly on Ubuntu systems. However, having a faster and more optimized setup can take some effort on these platforms. Thankfully, on NixOS it&rsquo;s not hard at all, as I&rsquo;ll show you.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ self<span style="color:#f92672">,</span> config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</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:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    nginx<span style="color:#f92672">.</span>virtualHosts <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;cloud.example.com&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        enableACME <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</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:#e6db74">&#34;onlyoffice.example.com&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        enableACME <span style="color:#f92672">=</span> <span style="color:#66d9ef">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>    nextcloud <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      hostName <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;cloud.example.com&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>       <span style="color:#75715e"># Need to manually increment with every major upgrade.</span>
</span></span><span style="display:flex;"><span>      package <span style="color:#f92672">=</span> pkgs<span style="color:#f92672">.</span>nextcloud27;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Let NixOS install and configure the database automatically.</span>
</span></span><span style="display:flex;"><span>      database<span style="color:#f92672">.</span>createLocally <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Let NixOS install and configure Redis caching automatically.</span>
</span></span><span style="display:flex;"><span>      configureRedis <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Increase the maximum file upload size to avoid problems uploading videos.</span>
</span></span><span style="display:flex;"><span>      maxUploadSize <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;16G&#34;</span>;
</span></span><span style="display:flex;"><span>      https <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      enableBrokenCiphersForSSE <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      autoUpdateApps<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      extraAppsEnable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      extraApps <span style="color:#f92672">=</span> <span style="color:#66d9ef">with</span> config<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>nextcloud<span style="color:#f92672">.</span>package<span style="color:#f92672">.</span>packages<span style="color:#f92672">.</span>apps; {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># List of apps we want to install and are already packaged in</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/nextcloud/packages/nextcloud-apps.json</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">inherit</span> calendar contacts mail notes onlyoffice tasks;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Custom app installation example.</span>
</span></span><span style="display:flex;"><span>        cookbook <span style="color:#f92672">=</span> pkgs<span style="color:#f92672">.</span>fetchNextcloudApp <span style="color:#66d9ef">rec</span> {
</span></span><span style="display:flex;"><span>          url <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;https://github.com/nextcloud/cookbook/releases/download/v0.10.2/Cookbook-0.10.2.tar.gz&#34;</span>;
</span></span><span style="display:flex;"><span>          sha256 <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;sha256-XgBwUr26qW6wvqhrnhhhhcN4wkI+eXDHnNSm1HDbP6M=&#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:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        overwriteProtocol <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https&#34;</span>;
</span></span><span style="display:flex;"><span>        defaultPhoneRegion <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;PT&#34;</span>;
</span></span><span style="display:flex;"><span>        dbtype <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;pgsql&#34;</span>;
</span></span><span style="display:flex;"><span>        adminuser <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;admin&#34;</span>;
</span></span><span style="display:flex;"><span>        adminpassFile <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/path/to/nextcloud-admin-pass&#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>    onlyoffice <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      hostname <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;onlyoffice.example.com&#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>You may want to proceed with caution while setting up the OnlyOffice server, which will allow for Google Docs-like functionality on our Nextcloud instance, by having it only accessible inside your VPN or by setting the <code>services.onlyoffice.jwtSecretFile</code> option if exposed to the public Internet.</p>
<p>With this snippet, a Nextcloud instance with a selection of pre-installed Apps, PostgreSQL as a database, Redis Caching and Let&rsquo;s Encrypt certificates will be set up for you.</p>
<p>To connect to the OnlyOffice server, configure it appropriately in Administration settings &gt; ONLYOFFICE &gt; ONLYOFFICE Docs address.</p>
<h2 id="backups">Backups</h2>
<p>In this configuration, we need to persist the <code>/var/lib/nextcloud</code> and <code>/var/lib/postgresql</code> directories.</p>
<p>For backing up, you could copy  <code>/var/lib/nextcloud</code> to another computer and, for the database, dump it to a file and copy it to another computer as well, as described <a href="https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html">in the official Nextcloud documentation</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Once again, NixOS proves itself as an amazing self-hosting platform.</p>
<p>Nextcloud, in its default configuration, is sometimes known for running slow.
Thanks to NixOS, we&rsquo;ve optimized its performance and that&rsquo;s quite impactful, as it&rsquo;s my most used self-hosted application.
Having all of these apps running on Nextcloud has enabled me to move on from GSuite to a mostly autonomous and self-hosted infrastructure.</p>
<p>In the future, I look forward to being able to use Collabora/Nextcloud Office instead of OnlyOffice, as it&rsquo;s more aligned with Nextcloud&rsquo;s philosophical goals and hasn&rsquo;t done <a href="https://help.nextcloud.com/t/onlyoffice-removed-web-mobile-editing-from-version-5-5-0-of-community-document-server/74360">suspicious decisions in the past</a>.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nixos.wiki/wiki/Nextcloud">https://nixos.wiki/wiki/Nextcloud</a></li>
<li><a href="https://nixos.wiki/wiki/Onlyoffice-Documentserver">https://nixos.wiki/wiki/Onlyoffice-Documentserver</a></li>
<li><a href="https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html">https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html</a></li>
<li><a href="https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html">https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Setting up Plausible Analytics on NixOS</title>
      <link>https://carlosvaz.com/posts/setting-up-plausible-analytics-on-nixos/</link>
      <pubDate>Thu, 03 Aug 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/setting-up-plausible-analytics-on-nixos/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been looking to set up analytics on my blog and maybe some other websites.&#xA;I&amp;rsquo;ve avoided Google Analytics as it&amp;rsquo;s quite insideous and would like to avoid giving them my readers&amp;rsquo; data willingly and for free.&lt;/p&gt;&#xA;&lt;p&gt;From my research, I found Plausible and Umami to be good alternatives.&#xA;Both collect so little data that cookie consent banners are not required and both are very minimalistic in their dashboards, focusing on what really matters.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve been looking to set up analytics on my blog and maybe some other websites.
I&rsquo;ve avoided Google Analytics as it&rsquo;s quite insideous and would like to avoid giving them my readers&rsquo; data willingly and for free.</p>
<p>From my research, I found Plausible and Umami to be good alternatives.
Both collect so little data that cookie consent banners are not required and both are very minimalistic in their dashboards, focusing on what really matters.</p>
<p>A friend of mine is using Umami and is quite happy with his setup.
Umami should also be lighter, as Plausible is designed to support a much larger number of connected websites.
Despite all of that, I ended up choosing Plausible simply because it has an upstream NixOS module, allowing me to set it up quite trivially, whereas I would have to package Umami and setup its PostgreSQL integration manually or use containers, which I&rsquo;m also trying to avoid in my self-hosted infrastructure.</p>
<p>This simple snippet is enough to set up a Plausible instance, all automatically configured for you.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ self<span style="color:#f92672">,</span> config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;plausible.example.com&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">in</span> {
</span></span><span style="display:flex;"><span>  age<span style="color:#f92672">.</span>secrets <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    plausibleAdminPassword<span style="color:#f92672">.</span>file <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>self<span style="color:#e6db74">}</span><span style="color:#e6db74">/secrets/plausibleAdminPassword.age&#34;</span>;
</span></span><span style="display:flex;"><span>    plausibleReleaseCookie<span style="color:#f92672">.</span>file <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>self<span style="color:#e6db74">}</span><span style="color:#e6db74">/secrets/plausibleReleaseCookie.age&#34;</span>;
</span></span><span style="display:flex;"><span>    plausibleSecretKeybase<span style="color:#f92672">.</span>file <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>self<span style="color:#e6db74">}</span><span style="color:#e6db74">/secrets/plausibleSecretKeybase.age&#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:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    nginx<span style="color:#f92672">.</span>virtualHosts<span style="color:#f92672">.</span><span style="color:#e6db74">${</span>domain<span style="color:#e6db74">}</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      enableACME <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      locations<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/&#34;</span><span style="color:#f92672">.</span>proxyPass <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;http://127.0.0.1:</span><span style="color:#e6db74">${</span>toString config<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>plausible<span style="color:#f92672">.</span>server<span style="color:#f92672">.</span>port<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    plausible <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      adminUser <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># activate is used to skip the email verification of the admin-user that&#39;s</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># automatically created by plausible. This is only supported if</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># postgresql is configured by the module. This is done by default, but</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># can be turned off with services.plausible.database.postgres.setup.</span>
</span></span><span style="display:flex;"><span>        activate <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        email <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;john.doe@example.com&#34;</span>;
</span></span><span style="display:flex;"><span>        passwordFile <span style="color:#f92672">=</span> config<span style="color:#f92672">.</span>age<span style="color:#f92672">.</span>secrets<span style="color:#f92672">.</span>plausibleAdminPassword<span style="color:#f92672">.</span>path;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      server <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        baseUrl <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://</span><span style="color:#e6db74">${</span>domain<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;
</span></span><span style="display:flex;"><span>        secretKeybaseFile <span style="color:#f92672">=</span> config<span style="color:#f92672">.</span>age<span style="color:#f92672">.</span>secrets<span style="color:#f92672">.</span>plausibleSecretKeybase<span style="color:#f92672">.</span>path;
</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 style="color:#75715e"># As this is a root on tmpfs system, we use the impermanence</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># NixOS module to persist state between reboots.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># You can omit the next block if using a regular configuration.</span>
</span></span><span style="display:flex;"><span>  environment<span style="color:#f92672">.</span>persistence<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/persist&#34;</span><span style="color:#f92672">.</span>directories <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>    [
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;/var/lib/postgresql&#34;</span>
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        directory <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/var/lib/clickhouse&#34;</span>;
</span></span><span style="display:flex;"><span>        mode <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;0750&#34;</span>;
</span></span><span style="display:flex;"><span>        user <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;clickhouse&#34;</span>;
</span></span><span style="display:flex;"><span>        group <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;clickhouse&#34;</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        directory <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/var/lib/private/plausible&#34;</span>;
</span></span><span style="display:flex;"><span>        mode <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;0750&#34;</span>;
</span></span><span style="display:flex;"><span>        user <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;plausible&#34;</span>;
</span></span><span style="display:flex;"><span>        group <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;plausible&#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>In this snippet I&rsquo;m using <a href="https://github.com/ryantm/agenix">agenix</a> to manage secrets such as the admin password.
You&rsquo;re obvisouly free to use your secrets management tool of choice or you can just use file paths which are not in public version control systems.</p>
<p>As per the official documentation, the <code>secretKeybaseFile</code>&rsquo;s contents should be generated with:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">$ openssl rand -base64 64 | tr -d &#39;\n&#39; ; echo</code></pre><p>For some unknown unreason, the admin password I set up didn&rsquo;t work after the rebuild.
I worked around that by using the reset password form on the login page.
In my case, this worked out of the box as the host where I deployed this Plausible instance was also a mailserver.
If yours isn&rsquo;t, you may have to adjust the <code>services.plausible.mail.*</code> options accordingly.</p>
<p>All that&rsquo;s left is to add a website in the dashboard.
You&rsquo;ll be presented with an HTML snippet which you should include on your website&rsquo;s <code>&lt;head&gt;</code> tag to start collecting privacy-respecting analytics.</p>
<p>As optional configuration, I enabled email alerts for weekly and monthly reports and also traffic spikes, to avoid creating the common addiction of being permanently looking at the analytics dashboard instead of engaging in productive activities.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I&rsquo;m quite happy with this setup.
As I said before, this gives me some insight into my readership without compromising their privacy.</p>
<p>For many months my only analytics were from the Google Search Console, as I was having configuration and compilation errros in the past when trying to set up Plausible in NixOS.
Recently I decided to give it another go for no special reason and was pleasantly surprised to find out that it just works.</p>
<p>It seems to be possible to <a href="https://plausible.io/docs/self-hosting-configuration#google-search-integration">integrate Plausible with the Google Search Console API</a> and that may be something I&rsquo;ll look into in the future.</p>
<p>If you wish to work around your readers&rsquo; adblockers, this also seems to be possible using a proxy as to not trigger the usual adblocker filters, as explained in <a href="https://www.ersocon.net/articles/plausible-js-adblocker-workaround-on-nixos~79dc0321-4d77-4e11-840e-73ef27df58ef">this article</a>.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nixos.org/manual/nixos/stable/#module-services-plausible">https://nixos.org/manual/nixos/stable/#module-services-plausible</a></li>
<li><a href="https://plausible.io/docs/self-hosting">https://plausible.io/docs/self-hosting</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Setting up a WordPress website on NixOS</title>
      <link>https://carlosvaz.com/posts/setting-up-a-wordpress-website-on-nixos/</link>
      <pubDate>Tue, 01 Aug 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/setting-up-a-wordpress-website-on-nixos/</guid>
      <description>&lt;p&gt;Another instance where NixOS makes it easy to self-host services, this time we&amp;rsquo;re setting up WordPress using the upstream nixpkgs module and some auxiliary functions to help us package themes and plugins.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ config&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; lib&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; pkgs&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt; }:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  domain &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Auxiliary functions&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  fetchPackage &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { name&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; version&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; hash&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; isTheme }:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pkgs&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stdenv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mkDerivation &lt;span style=&#34;color:#66d9ef&#34;&gt;rec&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;inherit&lt;/span&gt; name version hash;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      src &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; isTheme &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;theme&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;plugin&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; pkgs&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fetchzip {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;inherit&lt;/span&gt; name version hash;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://downloads.wordpress.org/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;type&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;version&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.zip&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      installPhase &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mkdir -p $out; cp -R * $out/&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  fetchPlugin &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { name&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; version&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; hash }:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (fetchPackage {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; name;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; version;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      hash &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hash;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      isTheme &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  fetchTheme &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { name&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; version&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; hash }:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (fetchPackage {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; name;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; version;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      hash &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hash;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      isTheme &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Plugins&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  google-site-kit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (fetchPlugin {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;google-site-kit&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.103.0&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hash &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sha256-8QZ4XTCKVdIVtbTV7Ka4HVMiUGkBYkxsw8ctWDV8gxs=&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Themes&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  astra &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (fetchTheme {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;astra&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4.1.5&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hash &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sha256-X3Jv2kn0FCCOPgrID0ZU8CuSjm/Ia/d+om/ShP5IBgA=&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  services &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    nginx&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;virtualHosts&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;domain&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      enableACME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      forceSSL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wordpress &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      webserver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nginx&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      sites&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;domain&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        plugins &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;inherit&lt;/span&gt; google-site-kit; };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        themes &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;inherit&lt;/span&gt; astra; };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        settings &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { WP_DEFAULT_THEME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;astra&amp;#34;&lt;/span&gt;; };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# As this is a root on tmpfs system, we use the impermanence&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# NixOS module to persist WordPress state between reboots.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# You can omit the next two lines if using a regular configuration.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  environment&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;persistence&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/persist&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;directories &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/var/lib/mysql&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/var/lib/wordpress&amp;#34;&lt;/span&gt; ];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I configure WordPress to use Nginx as the web server instead of httpd as it&amp;rsquo;s what I already use as a reverse proxy for my other self-hosted applications.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Another instance where NixOS makes it easy to self-host services, this time we&rsquo;re setting up WordPress using the upstream nixpkgs module and some auxiliary functions to help us package themes and plugins.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span>
</span></span><span style="display:flex;"><span>  domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;example.com&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Auxiliary functions</span>
</span></span><span style="display:flex;"><span>  fetchPackage <span style="color:#f92672">=</span> { name<span style="color:#f92672">,</span> version<span style="color:#f92672">,</span> hash<span style="color:#f92672">,</span> isTheme }:
</span></span><span style="display:flex;"><span>    pkgs<span style="color:#f92672">.</span>stdenv<span style="color:#f92672">.</span>mkDerivation <span style="color:#66d9ef">rec</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">inherit</span> name version hash;
</span></span><span style="display:flex;"><span>      src <span style="color:#f92672">=</span> <span style="color:#66d9ef">let</span> type <span style="color:#f92672">=</span> <span style="color:#66d9ef">if</span> isTheme <span style="color:#66d9ef">then</span> <span style="color:#e6db74">&#34;theme&#34;</span> <span style="color:#66d9ef">else</span> <span style="color:#e6db74">&#34;plugin&#34;</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">in</span> pkgs<span style="color:#f92672">.</span>fetchzip {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">inherit</span> name version hash;
</span></span><span style="display:flex;"><span>        url <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://downloads.wordpress.org/</span><span style="color:#e6db74">${</span>type<span style="color:#e6db74">}</span><span style="color:#e6db74">/</span><span style="color:#e6db74">${</span>name<span style="color:#e6db74">}</span><span style="color:#e6db74">.</span><span style="color:#e6db74">${</span>version<span style="color:#e6db74">}</span><span style="color:#e6db74">.zip&#34;</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      installPhase <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;mkdir -p $out; cp -R * $out/&#34;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  fetchPlugin <span style="color:#f92672">=</span> { name<span style="color:#f92672">,</span> version<span style="color:#f92672">,</span> hash }:
</span></span><span style="display:flex;"><span>    (fetchPackage {
</span></span><span style="display:flex;"><span>      name <span style="color:#f92672">=</span> name;
</span></span><span style="display:flex;"><span>      version <span style="color:#f92672">=</span> version;
</span></span><span style="display:flex;"><span>      hash <span style="color:#f92672">=</span> hash;
</span></span><span style="display:flex;"><span>      isTheme <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  fetchTheme <span style="color:#f92672">=</span> { name<span style="color:#f92672">,</span> version<span style="color:#f92672">,</span> hash }:
</span></span><span style="display:flex;"><span>    (fetchPackage {
</span></span><span style="display:flex;"><span>      name <span style="color:#f92672">=</span> name;
</span></span><span style="display:flex;"><span>      version <span style="color:#f92672">=</span> version;
</span></span><span style="display:flex;"><span>      hash <span style="color:#f92672">=</span> hash;
</span></span><span style="display:flex;"><span>      isTheme <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</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:#75715e"># Plugins</span>
</span></span><span style="display:flex;"><span>  google-site-kit <span style="color:#f92672">=</span> (fetchPlugin {
</span></span><span style="display:flex;"><span>    name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;google-site-kit&#34;</span>;
</span></span><span style="display:flex;"><span>    version <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;1.103.0&#34;</span>;
</span></span><span style="display:flex;"><span>    hash <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;sha256-8QZ4XTCKVdIVtbTV7Ka4HVMiUGkBYkxsw8ctWDV8gxs=&#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:#75715e"># Themes</span>
</span></span><span style="display:flex;"><span>  astra <span style="color:#f92672">=</span> (fetchTheme {
</span></span><span style="display:flex;"><span>    name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;astra&#34;</span>;
</span></span><span style="display:flex;"><span>    version <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;4.1.5&#34;</span>;
</span></span><span style="display:flex;"><span>    hash <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;sha256-X3Jv2kn0FCCOPgrID0ZU8CuSjm/Ia/d+om/ShP5IBgA=&#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:#66d9ef">in</span> {
</span></span><span style="display:flex;"><span>  services <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    nginx<span style="color:#f92672">.</span>virtualHosts<span style="color:#f92672">.</span><span style="color:#e6db74">${</span>domain<span style="color:#e6db74">}</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enableACME <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    wordpress <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      webserver <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nginx&#34;</span>;
</span></span><span style="display:flex;"><span>      sites<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>domain<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        plugins <span style="color:#f92672">=</span> { <span style="color:#66d9ef">inherit</span> google-site-kit; };
</span></span><span style="display:flex;"><span>        themes <span style="color:#f92672">=</span> { <span style="color:#66d9ef">inherit</span> astra; };
</span></span><span style="display:flex;"><span>        settings <span style="color:#f92672">=</span> { WP_DEFAULT_THEME <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;astra&#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 style="color:#75715e"># As this is a root on tmpfs system, we use the impermanence</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># NixOS module to persist WordPress state between reboots.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># You can omit the next two lines if using a regular configuration.</span>
</span></span><span style="display:flex;"><span>  environment<span style="color:#f92672">.</span>persistence<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/persist&#34;</span><span style="color:#f92672">.</span>directories <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>    [ <span style="color:#e6db74">&#34;/var/lib/mysql&#34;</span> <span style="color:#e6db74">&#34;/var/lib/wordpress&#34;</span> ];
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>I configure WordPress to use Nginx as the web server instead of httpd as it&rsquo;s what I already use as a reverse proxy for my other self-hosted applications.</p>
<p>After deploying this configuration, you&rsquo;ll be met with the famous 5-minute WordPress installation at your domain, with a MySQL database and Let&rsquo;s Encrypt automatically configured for you.
For demonstration, the Astra theme and the Google Site Kit plugin will also be automatically installed.</p>
<p>You&rsquo;re then presented with the usual WordPress dashboard, where you&rsquo;re free to enable the plugins we just installed and also do other miscellaneous configuration.
In theory, these steps can also be declarative using the <code>services.wordpress.sites.&lt;domain&gt;.extraConfig</code>, although I had trouble doing that and was met with database connection errors when rebuilding the configuration.</p>
<p>Beyond the ease of installation, there may be advantages to using WordPress on NixOS instead of a docker-compose setup, for example.
WordPress is known to quickly become a remote shell for intruders if not properly looked after and regularly updated.
On NixOS, the only themes and plugins the WordPress instance will have access to will be the ones we explicitly set in the NixOS module, preventing the installation of plugins with malware by some distracted administrator through the Web interface.
Thanks to the Nix store&rsquo;s properties of immutability and restricted permissions, it&rsquo;ll also be harder for malicious plugins to modify themselves when compared to normal setups.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Once again, NixOS made setting up another self-hosted service very easy.</p>
<p>This post will help you set up a basic WordPress instance, you&rsquo;re then free to install more plugins and to optimize the website further.
There are some suggestions on the relevant <a href="https://nixos.wiki/wiki/Wordpress#Tips_and_tricks">NixOS Wiki page</a>.</p>
<p>Although I felt I learned something with this experience, I still prefer using simple Static Site Generators like <a href="https://gohugo.io">Hugo</a> for my personal projects.
Using Hugo along with <a href="https://ox-hugo.scripter.co">ox-hugo</a> allows me to have a website that&rsquo;s quite &ldquo;declarative&rdquo;, configured with plain text files, where I can do easy version control and deploy anywhere, many times for free.
It also produces very fast sites without much effort, many times being close to a perfect score on the <a href="https://developer.chrome.com/docs/lighthouse/overview/">Lighthouse speed test</a>. I also can be more relieved about security and maintenance as it&rsquo;s just my webserver serving static files, without any backend or database to be exploited.</p>
<p>Nonetheless, about one third of the Web are WordPress websites and it&rsquo;s useful to know how to set them up and configure them on NixOS.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nixos.wiki/wiki/Wordpress">https://nixos.wiki/wiki/Wordpress</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Installing NixOS with root on tmpfs and encrypted ZFS on a netcup VPS</title>
      <link>https://carlosvaz.com/posts/installing-nixos-with-root-on-tmpfs-and-encrypted-zfs-on-a-netcup-vps/</link>
      <pubDate>Tue, 10 Jan 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/installing-nixos-with-root-on-tmpfs-and-encrypted-zfs-on-a-netcup-vps/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently ordered a netcup VPS.&#xA;Before, I had a Kimsufi server and although it had 2TB of storage for roughly 7€ per month, I found it too expensive and too slow for my needs.&lt;/p&gt;&#xA;&lt;p&gt;As I&amp;rsquo;ve now started hosting services at home, my VPS needs are only hosting a personal mailserver, my personal websites, a headscale server and to serve as a bastion server for some services I host at home that I want to be accessible from the outside world.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve recently ordered a netcup VPS.
Before, I had a Kimsufi server and although it had 2TB of storage for roughly 7€ per month, I found it too expensive and too slow for my needs.</p>
<p>As I&rsquo;ve now started hosting services at home, my VPS needs are only hosting a personal mailserver, my personal websites, a headscale server and to serve as a bastion server for some services I host at home that I want to be accessible from the outside world.</p>
<p>This means that I don&rsquo;t need anything too powerful and can go with something cheaper.
My final options were Hetzner Cloud CPX11, Scaleway Stardust, BuyVM Slice 1024 and the netcup VPS 200 G10s.</p>
<p>I ended up choosing the netcup VPS as it seemed to be the best value for the money.</p>
<p>If I weren&rsquo;t hosting email, I&rsquo;d strongly consider simply using Cloudflare Tunnel ou Tailscale Funnel for exposing some services to the public Internet and skip the VPS altogether.</p>
<p>The following post describes how I set up NixOS on this VPS.</p>
<h2 id="enabling-uefi-boot">Enabling UEFI boot</h2>
<p>The first step was to enable UEFI booting on the server.
Quite simple, just go to the VPS dashboard website &gt; Settings &gt; UEFI Settings &gt; Activate UEFI Boot.</p>
<h2 id="booting-the-nixos-iso">Booting the NixOS ISO</h2>
<p>Thankfully, netcup allows its users to boot their servers with their ISOs of choice.
If all you want is a standard NixOS setup, <a href="https://github.com/elitak/nixos-infect">nixos-infect</a> may be enough for your needs.
However, I want to install ZFS with encryption which requires formatting the existing partitions.
By being able to boot from the NixOS ISO, we skip the headache of installing NixOS through other exotic methods, such as installing from a rescue image or from running a kexec on the original deployed Linux distro (and that is a matter for another post).</p>
<p>This meant I had to download the NixOS ISO and upload it to their FTP server.
The credentials for uploading custom images can be found in Media &gt; DVD Drive &gt; Login data to FTP.</p>
<p>However, after running the usual SFTP command, I got the following error:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">❯ sftp -P &lt;sftp-port&gt; &lt;username&gt;@&lt;sftp-hostname&gt;
Unable to negotiate with &lt;sftp-hostname&gt; port &lt;sftp-port&gt;: no matching host key type found. Their offer: ssh-rsa,ssh-dss</code></pre><p>So I explicitly allowed <code>ssh-rsa</code> and it worked as expected:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">sftp -oHostKeyAlgorithms=+ssh-rsa -P &lt;sftp-port&gt; &lt;username&gt;@&lt;sftp-hostname&gt;
sftp&gt; put Downloads/nixos-minimal-22.11.1347.0bf3109eeb6-x86_64-linux.iso cdrom/</code></pre><p>Now, to boot from the uploaded ISO, we&rsquo;ll go to Own DVDs and click on attach DVD:</p>
<figure><img src="/netcup_dvd_boot.png">
</figure>

<p>And reboot the server.</p>
<h2 id="installing-nixos">Installing NixOS</h2>
<h3 id="networking-the-live-iso">Networking the live ISO</h3>
<p>After booting to NixOS on the server, I found it had no network connectivity.
So I connected through VNC through the control panel and am now in the NixOS live ISO.</p>
<p>We&rsquo;ll start by manually setting up the network, according to the Network section of the control panel:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">ip addr add &lt;server-ip&gt;/&lt;prefix-length&gt; dev &lt;ethernet-network-interface&gt;
ip route add default via &lt;gateway-ip&gt; dev &lt;ethernet-network-interface&gt;
echo &#34;nameserver 1.1.1.1&#34; &gt; /etc/resolv.conf</code></pre><p>We can now change the password for the root and finish the installation through SSH or just continue using the VNC window.</p>
<h3 id="partitioning">Partitioning</h3>
<p>Now, we&rsquo;ll wipe the drive and create its partitions:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">wipefs -a /dev/sda

parted -a optimal /dev/sda
(parted) unit mib
(parted) mklabel gpt
(parted) mkpart ESP fat32 1 513
(parted) set 1 boot on
(parted) mkpart primary 513 100%
(parted) quit</code></pre><p>We&rsquo;ll now format the <code>/boot</code> partition:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">mkfs.fat -F 32 -n boot /dev/sda1</code></pre><p>Finally, we&rsquo;ll create the ZFS pool for the persistent datasets:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">ls /dev/disk/by-id/* # Find the correct disk.
ls /dev/disk/by-path/* # ... or if the VM is using virtio

zpool create \
    -o ashift=12 \
    -o autotrim=on \
    -O acltype=posixacl \
    -O atime=off \
    -O canmount=off \
    -O compression=zstd \
    -O dnodesize=auto \
    -O normalization=formD \
    -O xattr=sa \
    -O mountpoint=none \
    -O encryption=on \
    -O keylocation=prompt \
    -O keyformat=passphrase \
    rpool /dev/disk/by-id/&lt;disk&gt;

zfs create -p -o refreservation=1G -o mountpoint=none rpool/local/reserved
zfs create -p rpool/local/nix
zfs create -p rpool/safe/persist</code></pre><p>We&rsquo;ll now start to mount our partitions and datasets so we can finally install NixOS:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">mount -t tmpfs none /mnt
mkdir -p /mnt/{boot,nix,persist}

mount /dev/sda1 /mnt/boot
mount -t zfs -o zfsutil rpool/local/nix /mnt/nix
mount -t zfs -o zfsutil rpool/safe/persist /mnt/persist</code></pre><h2 id="configuring-nixos">Configuring NixOS</h2>
<p>Let&rsquo;s start by generating a configuration:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">nixos-generate-config --root /mnt</code></pre><p>And here follows some specific configuration, in addition to the one we&rsquo;re used to.</p>
<h3 id="enable-zfs-support">Enable ZFS support:</h3>
<p><code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>boot<span style="color:#f92672">.</span>supportedFilesystems <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfs&#34;</span> ];
</span></span><span style="display:flex;"><span>boot<span style="color:#f92672">.</span>kernelPackages <span style="color:#f92672">=</span> config<span style="color:#f92672">.</span>boot<span style="color:#f92672">.</span>zfs<span style="color:#f92672">.</span>package<span style="color:#f92672">.</span>latestCompatibleLinuxPackages;
</span></span><span style="display:flex;"><span>networking<span style="color:#f92672">.</span>hostId <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;host-id&gt;</span>; <span style="color:#75715e"># For example: head -c 8 /etc/machine-id</span></span></span></code></pre></div><h3 id="set-up-the-root-user">Set up the root user</h3>
<p><code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span><span style="color:#75715e"># To generate a hash to put in initialHashedPassword</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># you can do this:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># $ nix-shell --run &#39;mkpasswd -m SHA-512 -s&#39; -p mkpasswd</span>
</span></span><span style="display:flex;"><span>users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span>root<span style="color:#f92672">.</span>initialPassword <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;hunter2&#34;</span>;
</span></span><span style="display:flex;"><span>users<span style="color:#f92672">.</span>mutableUsers <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;</span></span></code></pre></div><p>You can also just skip this part and set up remote access by SSH. Be careful not to get locked out.</p>
<h3 id="partition-and-dataset-configuration">Partition and dataset configuration</h3>
<p><code>/etc/nixos/hardware-configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;none&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;tmpfs&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;defaults&#34;</span> <span style="color:#e6db74">&#34;size=2G&#34;</span> <span style="color:#e6db74">&#34;mode=755&#34;</span> ];
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/boot&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/dev/disk/by-uuid/&lt;boot_partition_uuid&gt;&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;vfat&#34;</span>;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/nix&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;rpool/local/nix&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zfs&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfsutil&#34;</span> ];
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/persist&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;rpool/safe/persist&#34;</span>;
</span></span><span style="display:flex;"><span>    fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zfs&#34;</span>;
</span></span><span style="display:flex;"><span>    options <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfsutil&#34;</span> ];
</span></span><span style="display:flex;"><span>    neededForBoot <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><h3 id="swap">Swap</h3>
<p>Enabling swap is usually a <a href="https://chrisdown.name/2018/01/02/in-defence-of-swap.html">good idea</a>.
But we&rsquo;d also like to avoid having swap on disk, as it&rsquo;s really slow.
So we&rsquo;ll follow <a href="https://old.reddit.com/r/Fedora/comments/r4a4so/interesting_fedora_does_not_support_hibernation/hmfc763/">Fedora&rsquo;s lead</a> and set up swap on ZRAM:
<code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>zramSwap<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;</span></span></code></pre></div><h3 id="remote-access-through-ssh">Remote access through SSH</h3>
<p>To enable remote SSH access with public key authentication, we&rsquo;ll add the following:</p>
<p><code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>services<span style="color:#f92672">.</span>openssh <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  passwordAuthentication <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>  kbdInteractiveAuthentication <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>  hostKeys <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      bits <span style="color:#f92672">=</span> <span style="color:#ae81ff">4096</span>;
</span></span><span style="display:flex;"><span>      path <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/persist/etc/ssh/ssh_host_rsa_key&#34;</span>;
</span></span><span style="display:flex;"><span>      type <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;rsa&#34;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      path <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/persist/etc/ssh/ssh_host_ed25519_key&#34;</span>;
</span></span><span style="display:flex;"><span>      type <span style="color:#f92672">=</span> <span style="color:#e6db74">&#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>
</span></span><span style="display:flex;"><span>users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span>root<span style="color:#f92672">.</span>openssh<span style="color:#f92672">.</span>authorizedKeys<span style="color:#f92672">.</span>keys <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;ssh_public_key&#34;</span> ];</span></span></code></pre></div><h3 id="static-ip-configuration">Static IP configuration</h3>
<p>Because netcup doesn&rsquo;t provide DHCP, we&rsquo;ll need to manually set up networking:</p>
<p><code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>networking <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  networkmanager<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>  useDHCP <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  interfaces<span style="color:#f92672">.</span>ens3 <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    useDHCP <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    ipv4<span style="color:#f92672">.</span>addresses <span style="color:#f92672">=</span> [{
</span></span><span style="display:flex;"><span>      address <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&lt;ipv4_address&gt;&#34;</span>;
</span></span><span style="display:flex;"><span>      prefixLength <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;ipv4_prefix_length&gt;</span>;
</span></span><span style="display:flex;"><span>    }];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    ipv6<span style="color:#f92672">.</span>addresses <span style="color:#f92672">=</span> [{
</span></span><span style="display:flex;"><span>      address <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&lt;ipv6_address&gt;&#34;</span>;
</span></span><span style="display:flex;"><span>      prefixLength <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;ipv6_prefix_length&gt;</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>  defaultGateway <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&lt;ipv4_gateway&gt;&#34;</span>;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><h3 id="persistence">Persistence</h3>
<p>Because we have root on tmpfs, we&rsquo;ll need to persist specific files and folders we wish to keep. There are many ways to do this but I found using the <a href="https://github.com/nix-community/impermanence">impermanence</a> module quite straightforward:</p>
<p><code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>environment<span style="color:#f92672">.</span>persistence<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/persist&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  hideMounts <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  files <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;/etc/machine-id&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  directories <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;/var/log&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><h3 id="remotely-decrypting-zfs">Remotely decrypting ZFS</h3>
<p>We&rsquo;ll now setup remote unlocking of the ZFS pool so we don&rsquo;t need to VNC into the machine every time we reboot.</p>
<p>For this, we&rsquo;ll need to enable networking in the initrd, which envolves enabling the correct kernel module.
Not only that, if your provider doesn&rsquo;t provide DHCP, you&rsquo;ll also need to manually set up the static IPv4 address.</p>
<p><code>/etc/nixos/configuration.nix</code></p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>boot <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Set up static IPv4 address in the initrd.</span>
</span></span><span style="display:flex;"><span>  kernelParams <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;ip=&lt;ipv4_address&gt;::&lt;ipv4_gateway&gt;:&lt;ipv4_netmask&gt;::&lt;interface&gt;:none&#34;</span> ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  initrd <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Switch this to your ethernet&#39;s kernel module.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># You can check what module you&#39;re currently using by running: lspci -v</span>
</span></span><span style="display:flex;"><span>    kernelModules <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;virtio_pci&#34;</span> ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    network <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># This will use udhcp to get an ip address.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Make sure you have added the kernel module for your network driver to `boot.initrd.availableKernelModules`,</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># so your initrd can load it!</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Static ip addresses might be configured using the ip argument in kernel command line:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt</span>
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      ssh <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># To prevent ssh clients from freaking out because a different host key is used,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># a different port for ssh is useful (assuming the same host has also a regular sshd running)</span>
</span></span><span style="display:flex;"><span>        port <span style="color:#f92672">=</span> <span style="color:#ae81ff">2222</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># hostKeys paths must be unquoted strings, otherwise you&#39;ll run into issues with boot.initrd.secrets</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># the keys are copied to initrd from the path specified; multiple keys can be set</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># you can generate any number of host keys using</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># `ssh-keygen -t ed25519 -N &#34;&#34; -f /path/to/ssh_host_ed25519_key`</span>
</span></span><span style="display:flex;"><span>        hostKeys <span style="color:#f92672">=</span> [ <span style="color:#e6db74">/path/to/ssh_host_ed25519_key_initrd</span> ];
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># public ssh key used for login</span>
</span></span><span style="display:flex;"><span>        authorizedKeys <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;&lt;ssh_public_key&gt;&#34;</span> ];
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># this will automatically load the zfs password prompt on login</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># and kill the other prompt so boot can continue</span>
</span></span><span style="display:flex;"><span>      postCommands <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        cat &lt;&lt;EOF &gt; /root/.profile
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        if pgrep -x &#34;zfs&#34; &gt; /dev/null
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        then
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          zfs load-key -a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          killall zfs
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        else
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          echo &#34;zfs not running -- maybe the pool is taking some time to load for some unforseen reason.&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        fi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        EOF
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      &#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>Then, after NixOS is installed, to decrypt the pool:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">ssh -4 -p 2222 root@&lt;ipv4_address&gt;</code></pre><h2 id="installing-nixos">Installing NixOS</h2>
<p>We&rsquo;re finally ready to install NixOS:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">nixos-install --no-root-passwd --root /mnt
umount -Rl /mnt
zpool export -a</code></pre><p>And don&rsquo;t forget to detach the DVD before rebooting.</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">reboot</code></pre><h2 id="closing-remarks">Closing remarks</h2>
<p>Although this initial setup took some work, I&rsquo;m happy with the final result.</p>
<p>By <a href="https://grahamc.com/blog/erase-your-darlings">erasing our darlings</a>, we keep our system clean and fresh.
It&rsquo;s also reassuring to know that everything that&rsquo;s running is explicitly set in the NixOS configuration and that we can just move it to some other host.
This also makes backups much easier, as I know exactly what needs to be kept.</p>
<p>I intend to have all my future servers set up like this.
Hopefully, following this post will make it easier.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://elis.nu/blog/2019/08/encrypted-zfs-mirror-with-mirrored-boot-on-nixos/">https://elis.nu/blog/2019/08/encrypted-zfs-mirror-with-mirrored-boot-on-nixos/</a></li>
<li><a href="https://elis.nu/blog/2020/05/nixos-tmpfs-as-root">https://elis.nu/blog/2020/05/nixos-tmpfs-as-root</a></li>
<li><a href="https://nixos.wiki/wiki/ZFS">https://nixos.wiki/wiki/ZFS</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Setting up wildcard Let&#39;s Encrypt certificates on NixOS</title>
      <link>https://carlosvaz.com/posts/setting-up-wildcard-lets-encrypt-certificates-on-nixos/</link>
      <pubDate>Mon, 26 Dec 2022 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/setting-up-wildcard-lets-encrypt-certificates-on-nixos/</guid>
      <description>&lt;p&gt;After setting up an Headscale server in order to have a mesh-VPN for my devices, I was now able to set up self-hosted services on my home server and access them anywhere in the world.&lt;/p&gt;&#xA;&lt;p&gt;However, because they&amp;rsquo;re inside this private network, I&amp;rsquo;m not able to get Let&amp;rsquo;s Encrypt certificates through the usual HTTP-01 challenge, exactly because the home server is not accessible from the public Internet.&lt;/p&gt;&#xA;&lt;p&gt;Because of this, and to prevent my browsers from screaming that I&amp;rsquo;m acessing my services through an &amp;ldquo;Insecure Connection&amp;rdquo; (even though the encrypted connection provided by Tailscale is plenty secure), I decided to set up wildcard certificates, provided by Let&amp;rsquo;s Encrypt.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>After setting up an Headscale server in order to have a mesh-VPN for my devices, I was now able to set up self-hosted services on my home server and access them anywhere in the world.</p>
<p>However, because they&rsquo;re inside this private network, I&rsquo;m not able to get Let&rsquo;s Encrypt certificates through the usual HTTP-01 challenge, exactly because the home server is not accessible from the public Internet.</p>
<p>Because of this, and to prevent my browsers from screaming that I&rsquo;m acessing my services through an &ldquo;Insecure Connection&rdquo; (even though the encrypted connection provided by Tailscale is plenty secure), I decided to set up wildcard certificates, provided by Let&rsquo;s Encrypt.</p>
<h2 id="nixos-configuration">NixOS configuration</h2>
<p>As usual, the NixOS configuration part of this setup is not too difficult:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ self<span style="color:#f92672">,</span> config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  security<span style="color:#f92672">.</span>acme <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    acceptTerms <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    defaults<span style="color:#f92672">.</span>email <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;john.doe@example.com&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    certs<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;example.com&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;example.com&#34;</span>;
</span></span><span style="display:flex;"><span>      extraDomainNames <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;*.example.com&#34;</span> ];
</span></span><span style="display:flex;"><span>      dnsProvider <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;ovh&#34;</span>;
</span></span><span style="display:flex;"><span>      dnsPropagationCheck <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      credentialsFile <span style="color:#f92672">=</span> <span style="color:#e6db74">/path/to/secrets/file</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>  users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span>nginx<span style="color:#f92672">.</span>extraGroups <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;acme&#34;</span> ];
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>In this example, I set my provider to OVH. You may want to change this to <a href="https://go-acme.github.io/lego/dns/">your provider of choice</a>.</p>
<p>Also worthy of note is that we need to add the <code>nginx</code> user to the <code>acme</code> group, so that nginx can read the certificate files created by the acme service.
Adjust this according to your preference of reverse proxy software.</p>
<h2 id="ovh-api-credentials">OVH API credentials</h2>
<p>The DNS-01 challenge, as the name implies, requires setting up DNS records to prove ownership of resources.
Because of that, we&rsquo;ll need to set up an API key for the acme NixOS module, so that it can set up these records automatically.</p>
<p>To create these credentials:</p>
<ol>
<li>Go to <a href="https://eu.api.ovh.com/createToken/">https://eu.api.ovh.com/createToken/</a></li>
<li>Fill the form with the required information as shown below:
<ul>
<li>Account ID or email address: usual OVH login</li>
<li>Password: usual OVH password</li>
<li>Script Name: for example, &ldquo;NixOS wildcard certificate&rdquo;</li>
<li>Script description: for example, &ldquo;NixOS wildcard certificate&rdquo;</li>
<li>Validity: Unlimited</li>
<li>Rights: use the + button to add the following lines
<ul>
<li><code>POST</code> : <code>/domain/zone/*</code></li>
<li><code>DELETE</code> : <code>/domain/zone/*</code></li>
</ul>
</li>
</ul>
</li>
</ol>
<p>After creating them, we&rsquo;ll need to place them in the <code>/path/to/secrets/file</code>.</p>
<p>Be careful not to share these publicly.
I personally use <a href="https://github.com/ryantm/agenix">agenix</a> for managing secrets in my NixOS configuration.</p>
<p>As per the <a href="https://go-acme.github.io/lego/dns/ovh/">OVH lego provider documentation</a>, this file will include something like this:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">OVH_ENDPOINT=ovh-eu
OVH_APPLICATION_KEY=&lt;application-key&gt;
OVH_APPLICATION_SECRET=&lt;application-secret&gt;
OVH_CONSUMER_KEY=&lt;consumer-key&gt;</code></pre><h2 id="dns-zone-configuration">DNS zone configuration</h2>
<p>Initially, I tried to set up DNS like this:</p>
<ul>
<li>Create a wildcard CNAME record so that <code>*.example.com</code> points to <code>home-server.tailnet.example.com</code>.</li>
</ul>
<p>However, with this setup, the acme service failed to fetch the certificate.</p>
<p>I ended up setting DNS this way instead:</p>
<ul>
<li>Create a wildcard A record so that <code>*.example.com</code> points to <code>100.64.0.1</code>, my home-server&rsquo;s Tailscale IP.</li>
</ul>
<p>Now everything worked perfectly, although I feel that the CNAME method was more elegant, as it didn&rsquo;t depend on the IP Tailscale assigned to my machine but only on its hostname and headscale&rsquo;s <a href="https://tailscale.com/kb/1081/magicdns/">MagicDNS</a>.</p>
<h2 id="using-the-certificate">Using the certificate</h2>
<p>Now, after running <code>nixos-rebuild switch</code> and making sure that no errors showed up while getting the certificate, all that&rsquo;s left is to start using it.</p>
<p>A simple nginx configuration that uses this certificate would like the following:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</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:#f92672">.</span>nginx <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    virtualHosts <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;some-service.example.com&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        useACMEHost <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;example.com&#34;</span>;
</span></span><span style="display:flex;"><span>        locations<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/&#34;</span><span style="color:#f92672">.</span>proxyPass <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;http://127.0.0.1:12345&#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></code></pre></div><h2 id="closing-remarks">Closing remarks</h2>
<p>Once again, NixOS made this process really straightforward.</p>
<p>I have read that Caddy makes this workflow really easy as well, if I weren&rsquo;t using NixOS I&rsquo;d probably be using that instead.</p>
<p>This setup is now ready to provide self-hosted services only accessible inside the headscale mesh-VPN and without the headache of using self-signed certificates or skipping the &ldquo;Insecure Connection&rdquo; warnings all the time.
It also didn&rsquo;t require port-forwarding on my router and having my services available to the hostile and chaotic public Internet.</p>
<p>Another subtle consequence of using wildcard certificates is that there&rsquo;s improved privacy in what concerns <a href="https://en.wikipedia.org/wiki/Certificate_Transparency">certificate transparency</a>.
When someone searches for your domain at a service such as <a href="https://crt.sh/">crt.sh</a>, all they&rsquo;ll see is that a wildcard certificate was emitted for that domain.
This way, you&rsquo;ll avoid having a public list of all the private services you&rsquo;ve set up at home.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns">https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns</a></li>
<li><a href="https://discourse.nixos.org/t/setup-a-wildcard-certificate-with-acme-on-a-custom-domain-name-hosted-by-powerdns/15055">https://discourse.nixos.org/t/setup-a-wildcard-certificate-with-acme-on-a-custom-domain-name-hosted-by-powerdns/15055</a></li>
<li><a href="https://go-acme.github.io/lego/dns/ovh/">https://go-acme.github.io/lego/dns/ovh/</a></li>
<li><a href="https://yunohost.org/en/providers/registrar/ovh/autodns">https://yunohost.org/en/providers/registrar/ovh/autodns</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Setting up Headscale on NixOS</title>
      <link>https://carlosvaz.com/posts/setting-up-headscale-on-nixos/</link>
      <pubDate>Sun, 11 Dec 2022 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/setting-up-headscale-on-nixos/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently bought a used USFF computer at a good price, inspired by the &lt;a href=&#34;https://www.servethehome.com/introducing-project-tinyminimicro-home-lab-revolution/&#34;&gt;TinyMiniMicro project&lt;/a&gt;.&#xA;It&amp;rsquo;s super fast, has plenty of storage and memory (32GB of RAM and 2TB of NVMe SSD storage) and, more importantly, is almost completely silent and consumes almost no electricity (~12W in idle, about 2x Raspberry Pi 4).&lt;/p&gt;&#xA;&lt;p&gt;So I decided to start moving my self-hosted applications to this machine, which will become my home server.&#xA;I will begin using my Kimsufi dedicated server only as an email server and as my only system open to the public Internet, as it has a static public IP address and OVH will know how to deal with DDOS attacks and such better than me.&#xA;Even though the Kimsufi machine is at an unbeatable price, around 7€ per month for 2TB of storage, its processor is incredibly slow and under-powered, while also having its networking capped at 100MBps.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve recently bought a used USFF computer at a good price, inspired by the <a href="https://www.servethehome.com/introducing-project-tinyminimicro-home-lab-revolution/">TinyMiniMicro project</a>.
It&rsquo;s super fast, has plenty of storage and memory (32GB of RAM and 2TB of NVMe SSD storage) and, more importantly, is almost completely silent and consumes almost no electricity (~12W in idle, about 2x Raspberry Pi 4).</p>
<p>So I decided to start moving my self-hosted applications to this machine, which will become my home server.
I will begin using my Kimsufi dedicated server only as an email server and as my only system open to the public Internet, as it has a static public IP address and OVH will know how to deal with DDOS attacks and such better than me.
Even though the Kimsufi machine is at an unbeatable price, around 7€ per month for 2TB of storage, its processor is incredibly slow and under-powered, while also having its networking capped at 100MBps.</p>
<p>This new home server will be faster while also being more in line with the philosophy of self-hosting, to be the owner of my hardware.
Not only that, it will also have better networking, not only speed-wise but also because it&rsquo;ll be geographically closer to me.</p>
<h2 id="headscale">Headscale</h2>
<p>But I don&rsquo;t want to have applications running at home being exposed to the public internet, where I would use DynamicDNS and port-forwarding on my router.
The Internet can be very hostile and chaotic, so I&rsquo;ll want my services to only be accessible to people I know and trust.
Well, this is the pitch for using a mesh-VPN, where we build a private network where devices can communicate with each other wherever they are, but are inaccessible from the rest of the Internet.</p>
<p>I have already used Nebula in the past as a mesh-VPN solution.
It&rsquo;s a self-hosted solution, is well integrated with NixOS and has an Android client.
However, after the initial setup, I felt too much friction to add new devices to the network.
I would need to generate keys for each device and then transfer them to my laptop, where I had my CA, sign the keys and then transfer them back.</p>
<p>All over the Web, there has been much talk about this new product called Tailscale.
It was also designed with this usecase in mind and many have described as a new <a href="https://en.wikipedia.org/wiki/LogMeIn_Hamachi">Hamachi</a>, which I have used in the past for having local Minecraft servers for playing with friends.</p>
<p>Although many of Tailscale&rsquo;s components are open-source, especially its clients, the server is closed-source.
It&rsquo;s more or less understandable that it works this way. They provide Tailscale as a SaaS product, especially directed at the enterprise sector, and this allows them to keep developing and innovating.
If they made it all open-source, they would risk ending up in a Docker situation, where they would struggle to monetize their oferring.</p>
<p>However, I was looking for free and open-source solution, hosted by myself.
I then found out that there&rsquo;s an open-source implementation of the Tailscale server, called Headscale (perfect naming).
Sometimes, it evens gets contributions from Tailscale employees.
<a href="https://github.com/kradalby">One of its most important maintainers</a> currently works at Tailscale.</p>
<p>Excellent, Headscale is also well integrated with NixOS and I could basically have all the advantages of using Tailscale such as MagicDNS, Taildrop, Exit Nodes, etc. while only using open-source components and a server managed by myself.</p>
<h2 id="installing-headscale-on-nixos">Installing Headscale on NixOS</h2>
<p>I couldn&rsquo;t find any post showing me how to setup a Headscale server on NixOS.
I only managed to find <a href="https://git.sr.ht/~misterio/nix-config/tree/main/item/hosts/electra/services/headscale.nix">another person&rsquo;s configuration</a>.
Turns out, it wasn&rsquo;t that hard.
All that was needed was to activate the Headscale service as set the relevant options:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;headscale.example.com&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">in</span> {
</span></span><span style="display:flex;"><span>  services <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    headscale <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      address <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;0.0.0.0&#34;</span>;
</span></span><span style="display:flex;"><span>      port <span style="color:#f92672">=</span> <span style="color:#ae81ff">8080</span>;
</span></span><span style="display:flex;"><span>      server_url <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://</span><span style="color:#e6db74">${</span>domain<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;
</span></span><span style="display:flex;"><span>      dns <span style="color:#f92672">=</span> { baseDomain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;example.com&#34;</span>; };
</span></span><span style="display:flex;"><span>      settings <span style="color:#f92672">=</span> { logtail<span style="color:#f92672">.</span>enabled <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>; };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    nginx<span style="color:#f92672">.</span>virtualHosts<span style="color:#f92672">.</span><span style="color:#e6db74">${</span>domain<span style="color:#e6db74">}</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      enableACME <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      locations<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        proxyPass <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;http://localhost:</span><span style="color:#e6db74">${</span>toString config<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>headscale<span style="color:#f92672">.</span>port<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;
</span></span><span style="display:flex;"><span>        proxyWebsockets <span style="color:#f92672">=</span> <span style="color:#66d9ef">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>
</span></span><span style="display:flex;"><span>  environment<span style="color:#f92672">.</span>systemPackages <span style="color:#f92672">=</span> [ config<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>headscale<span style="color:#f92672">.</span>package ];</span></span></code></pre></div><p><em>2024/04/27 Update</em>: A reader pointed out that the option <code>serverUrl</code> is now <code>server_url</code>, and that the error wasn&rsquo;t too obvious. It&rsquo;s now updated above.</p>
<p>And it just worked.
I should note that the option <code>proxyWebsockets</code> is need according to the <a href="https://github.com/juanfont/headscale/blob/main/docs/reverse-proxy.md#websockets">docs</a>.</p>
<p>All that&rsquo;s left is to create our first namespace, to which we&rsquo;ll our nodes later on:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">headscale namespaces create &lt;namespace_name&gt;</code></pre><h2 id="using-the-tailscale-client-and-daemon-on-nixos">Using the Tailscale client and daemon on NixOS</h2>
<p>Using the Tailscale client on NixOS is also really easy.
It&rsquo;s just a matter of activating the following configuration:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>services<span style="color:#f92672">.</span>tailscale<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>networking<span style="color:#f92672">.</span>firewall <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  checkReversePath <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;loose&#34;</span>;
</span></span><span style="display:flex;"><span>  trustedInterfaces <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;tailscale0&#34;</span> ];
</span></span><span style="display:flex;"><span>  allowedUDPPorts <span style="color:#f92672">=</span> [ config<span style="color:#f92672">.</span>services<span style="color:#f92672">.</span>tailscale<span style="color:#f92672">.</span>port ];
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><p>And running the command to add the node to the network:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">tailscale up --login-server &lt;headscale_url&gt;</code></pre><p>This command will output a link with instructions to add the node to the specified namespace on the server-side, which will look something like:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">headscale --namespace &lt;namespace_name&gt; nodes register --key &lt;machine_key&gt;</code></pre><p>Where <code>&lt;machine_key&gt;</code> is the string at the end of the URL shown by the Tailscale client.</p>
<p>After that, I noticed that, by default, the node names end up being the hostname followed by a string of random characters.
In order to make the names more legible and memorable, I chose to change them to be the machines&rsquo; hostname only.
I have read that using the option <code>--hostname</code> when running the <code>tailscale up</code> command sets the hostname right away but I haven&rsquo;t tested it yet.</p>
<p>To rename a single node on headscale:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">headscale nodes list
headscale node rename -i &lt;node_id&gt; &lt;new_name&gt;</code></pre><h2 id="magicdns">MagicDNS</h2>
<p>As I use systemd-resolved on my systems, Tailscale takes care of setting up all of the DNS-related configuration.
However, if I didn&rsquo;t use systemd-resolved, I&rsquo;d have to configure the DNS myself, as explained <a href="https://tailscale.com/kb/1063/install-nixos/#using-magicdns">here</a>.</p>
<p>Thanks to MagicDNS, after setting up the clients, we can connect to any node in the network, wherever we are in the world, by referring to them as &lt;name&gt;.&lt;namespace&gt;.&lt;baseDomain&gt;.
For example:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">ping node1.headnet.example.com</code></pre><h2 id="using-the-tailscale-android-client-on-grapheneos">Using the Tailscale Android client on GrapheneOS</h2>
<p>Setting up Tailscale on Android is also supposed to be quite straightforward, especially because Tailscale added a way to use external servers, allowing Headscale users to use the same client (<a href="https://github.com/juanfont/headscale/blob/main/docs/android-client.md#connecting-an-android-client">link</a>).</p>
<p>However, every time I tapped the &ldquo;Save and Restart&rdquo; button, the app would crash immediately.
When I restarted it, the sign-in button kept redirecting me to Tailscale&rsquo;s login page, which led me to believe it didn&rsquo;t actually configure the app to use my Headscale server.</p>
<p>Another user seemed to have just the same problem (<a href="https://forum.tailscale.com/t/tailscale-with-headscale-with-graphene-os/3312">link</a>), but there were no answers.</p>
<p>My remaining option was to modify the client&rsquo;s source code in order to use my Headscale server by default.
It was a quite simple modification (<a href="https://github.com/carjorvaz/tailscale-android">link</a>) but it requires me to sync my own fork and build the app every time there&rsquo;s an update.</p>
<p>To make matters worse, Tailscale&rsquo;s client is also incompatible with the Private DNS setting on Android, which I used in order to have DNS-level ad-blocking, using <a href="https://adguard-dns.io/en/public-dns.html">Adguard</a>.
I&rsquo;ll have to investigate further if it makes sense to set up a DNS server on my home server and use it on my Android device so I can have ad-blocking again (<a href="https://adguard-dns.io/en/public-dns.html">according to Daniel Micay, this is the only reasonable way to have system-wide ad-blocking on an Android device</a>).</p>
<h2 id="closing-remarks">Closing remarks</h2>
<p>The overall feeling of this experiment was that setting all of this up was very straightforward, with the exception of the Android client on my phone. Admittedly, it&rsquo;s also a more esoteric setup than usual (GrapheneOS).</p>
<p>In contrast to Nebula, adding new devices to the network is a matter of running a command both on the client and on the server (or tapping a button if on an Android device) and not of sending files around.</p>
<p>Also, MagicDNS and Taildrop just work and are great additions.</p>
<p>All that&rsquo;s left is to set up services on my home-server, in order for this mesh-VPN be of any use.</p>
<p>It would be interesing to keep having Let&rsquo;s Encrypt SSL certificates on my services, running inside the private network.
However, this will be harder than usual as I won&rsquo;t be able to complete the HTTP-01 challenge.
I&rsquo;ll need to setup the DNS-01 challenge which will allow me to use wildcard certificates. But that&rsquo;s a matter for a future post.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Installing NixOS on the Raspberry Pi 4 with UEFI and ZFS</title>
      <link>https://carlosvaz.com/posts/nixos-on-raspberry-pi-4-with-uefi-and-zfs/</link>
      <pubDate>Tue, 10 May 2022 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/nixos-on-raspberry-pi-4-with-uefi-and-zfs/</guid>
      <description>&lt;p&gt;After spending some time with &lt;a href=&#34;https://osmc.tv/&#34;&gt;OSMC&lt;/a&gt; on my Raspberry Pi 4, I&amp;rsquo;ve been meaning to install NixOS on it as it has been my distribution of choice on all my other systems.&lt;/p&gt;&#xA;&lt;p&gt;Installing &amp;ldquo;vanilla&amp;rdquo; NixOS on the Pi isn&amp;rsquo;t too difficult, just follow the &lt;a href=&#34;https://nixos.wiki/wiki/NixOS_on_ARM#SD_card_images_.28SBCs_and_similar_platforms.29&#34;&gt;installation steps on the wiki&lt;/a&gt;: download the SD card image, decompress it, flash the image to the SD card and you&amp;rsquo;re good to go.&lt;/p&gt;&#xA;&lt;p&gt;However, it gets more complicated when you try to go off the beaten path, such as when trying to do full-disk encryption or using filesystems other than ext4.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>After spending some time with <a href="https://osmc.tv/">OSMC</a> on my Raspberry Pi 4, I&rsquo;ve been meaning to install NixOS on it as it has been my distribution of choice on all my other systems.</p>
<p>Installing &ldquo;vanilla&rdquo; NixOS on the Pi isn&rsquo;t too difficult, just follow the <a href="https://nixos.wiki/wiki/NixOS_on_ARM#SD_card_images_.28SBCs_and_similar_platforms.29">installation steps on the wiki</a>: download the SD card image, decompress it, flash the image to the SD card and you&rsquo;re good to go.</p>
<p>However, it gets more complicated when you try to go off the beaten path, such as when trying to do full-disk encryption or using filesystems other than ext4.</p>
<p>After some reading and some trial and error, here are the steps I took to get NixOS with ZFS on the Pi, with UEFI booting.</p>
<h2 id="requirements">Requirements</h2>
<p>Make sure you&rsquo;ve enabled support for USB booting on your Raspberry Pi. If yours is considerably older, you might need to update its EEPROM before you can enable support for USB booting. For more details, check the <a href="https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#usb-mass-storage-boot">documentation</a>.</p>
<h2 id="installation-steps">Installation steps</h2>
<p>I started by booting the Pi off of a USB stick with the <a href="https://hydra.nixos.org/job/nixos/release-21.11/nixos.sd_image.aarch64-linux">generic SD card AArch64 image</a> but it would work just as well by initially booting off of an SD card.</p>
<p>As my installation target was a SATA SSD, I now connected it to one of the USB 3.0 ports on the Pi. I find using the Raspberry Pi with an SSD makes it feel more like a real computer, as the SSD is faster, has more storage capacity and will most certainly be more durable than a flimsy SD card, especially now that I&rsquo;ll also be using the Pi as a home server.</p>
<h3 id="partitioning">Partitioning</h3>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>wipefs -a /dev/sda
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>parted -a optimal /dev/sda -- mklabel gpt
</span></span><span style="display:flex;"><span>parted -a optimal /dev/sda -- mkpart ESP fat32 1MiB 513MiB
</span></span><span style="display:flex;"><span>parted -a optimal /dev/sda -- set <span style="color:#ae81ff">1</span> esp on
</span></span><span style="display:flex;"><span>parted -a optimal /dev/sda -- mkpart primary linux-swap 513MiB 8705MiB
</span></span><span style="display:flex;"><span>parted -a optimal /dev/sda -- mkpart primary 8705MiB 100%
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mkfs.fat -F <span style="color:#ae81ff">32</span> -n boot /dev/sda1
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mkswap -L swap /dev/sda2
</span></span><span style="display:flex;"><span>swapon /dev/sda2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>zpool create -O mountpoint<span style="color:#f92672">=</span>none -O atime<span style="color:#f92672">=</span>off -o ashift<span style="color:#f92672">=</span><span style="color:#ae81ff">12</span> -O acltype<span style="color:#f92672">=</span>posixacl -O xattr<span style="color:#f92672">=</span>sa -O compression<span style="color:#f92672">=</span>zstd -O dnodesize<span style="color:#f92672">=</span>auto -O normalization<span style="color:#f92672">=</span>formD zroot /dev/sda3
</span></span><span style="display:flex;"><span>zfs create -o refreservation<span style="color:#f92672">=</span>1G -o mountpoint<span style="color:#f92672">=</span>none zroot/reserved
</span></span><span style="display:flex;"><span>zfs create zroot/root
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mount -t zfs -o zfsutil zroot/root /mnt
</span></span><span style="display:flex;"><span>mkdir -p /mnt/boot
</span></span><span style="display:flex;"><span>mount /dev/sda1 /mnt/boot</span></span></code></pre></div><h3 id="uefi-setup">UEFI setup</h3>
<p>We&rsquo;ll now setup UEFI on the Pi. We do this by placing the files from the <a href="https://github.com/pftf/RPi4">Raspberry Pi 4 UEFI Firmware Images</a> on the boot partition:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>nix-shell -p wget unzip
</span></span><span style="display:flex;"><span>cd /mnt/boot
</span></span><span style="display:flex;"><span>wget https://github.com/pftf/RPi4/releases/download/v1.33/RPi4_UEFI_Firmware_v1.33.zip
</span></span><span style="display:flex;"><span>unzip RPi4_UEFI_Firmware_v1.33.zip
</span></span><span style="display:flex;"><span>rm README.md
</span></span><span style="display:flex;"><span>rm RPi4_UEFI_Firmware_v1.33.zip</span></span></code></pre></div><p><em>2022/11/04 Update:</em> As a reader pointed out, you can also do this step by just copying the files on the installation media (assuming <code>/dev/sda</code> is your installation disk):</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir /firmware
</span></span><span style="display:flex;"><span>mount /dev/sda1 /firmware
</span></span><span style="display:flex;"><span>cp /firmware/* /mnt/boot</span></span></code></pre></div><h3 id="nixos-installation">NixOS installation</h3>
<p>As per usual, we let NixOS generate a config for us:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>nixos-generate-config --root /mnt</span></span></code></pre></div><p>As we&rsquo;re using ZFS, we&rsquo;ll need to enable ZFS support:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>boot<span style="color:#f92672">.</span>supportedFilesystems <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfs&#34;</span> ];
</span></span><span style="display:flex;"><span>networking<span style="color:#f92672">.</span>hostId <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&lt;8 random numbers&gt;&#34;</span>;</span></span></code></pre></div><p>And also specify the <code>zfsutil</code> mount option for each of our datasets (in the <code>/mnt/etc/nixos/hardware-configuration.nix</code> file):</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>fileSystems<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zroot/root&#34;</span>;
</span></span><span style="display:flex;"><span>  fsType <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;zfs&#34;</span>;
</span></span><span style="display:flex;"><span>  options <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;zfsutil&#34;</span> ];
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><p>Now, as we&rsquo;re booting with UEFI, we&rsquo;ll delete the following line which is generated by default:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>boot<span style="color:#f92672">.</span>loader<span style="color:#f92672">.</span>generic-extlinux-compatible<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;</span></span></code></pre></div><p>&hellip;and we&rsquo;ll install a UEFI bootloader:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>boot<span style="color:#f92672">.</span>loader<span style="color:#f92672">.</span>efi<span style="color:#f92672">.</span>canTouchEfiVariables <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>boot<span style="color:#f92672">.</span>loader<span style="color:#f92672">.</span>systemd-boot<span style="color:#f92672">.</span>enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;</span></span></code></pre></div><p>Finally, we&rsquo;ll install NixOS:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>nixos-install</span></span></code></pre></div><h3 id="closing-remarks">Closing remarks</h3>
<p>To my surprise, I found that by using UEFI I could more easily achieve non-standard setups with NixOS on the Pi. I had some attempts at trying to install NixOS with ZFS with the usual booting setup on the Pi but I could never seem to boot it after the installation.</p>
<p>I should also note that setting up encryption and remote unlocking is not very difficult on this setup, as it only requires a little bit more of ZFS configuration.</p>
<p>After setting this up, a friend of mine pointed me to the following links:</p>
<ul>
<li><a href="https://discourse.nixos.org/t/planning-for-a-better-nixos-on-arm/15346">Planning for a better NixOS on ARM</a></li>
<li><a href="https://nixos.wiki/wiki/NixOS_on_ARM/UEFI">NixOS on ARM/UEFI</a></li>
</ul>
<p>It turns out that I accidentally ended up building an ARM system which is aligned with the NixOS ARM lead&rsquo;s view for NixOS ARM systems in the future, which I find amusing.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://mgdm.net/weblog/nixos-on-raspberry-pi-4">https://mgdm.net/weblog/nixos-on-raspberry-pi-4</a></li>
<li><a href="https://nixos.wiki/wiki/NixOS_on_ARM">https://nixos.wiki/wiki/NixOS_on_ARM</a></li>
<li><a href="https://nixos.wiki/wiki/NixOS_on_ARM/Raspberry_Pi_4">https://nixos.wiki/wiki/NixOS_on_ARM/Raspberry_Pi_4</a></li>
<li><a href="https://elis.nu/blog/2019/08/encrypted-zfs-mirror-with-mirrored-boot-on-nixos/">https://elis.nu/blog/2019/08/encrypted-zfs-mirror-with-mirrored-boot-on-nixos/</a></li>
<li><a href="https://nixos.wiki/wiki/ZFS">https://nixos.wiki/wiki/ZFS</a></li>
<li><a href="https://nixos.org/manual/nixos/stable/index.html#sec-installation">https://nixos.org/manual/nixos/stable/index.html#sec-installation</a></li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
