<?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>Self-Hosting on Carlos Vaz</title>
    <link>https://carlosvaz.com/tags/self-hosting/</link>
    <description>Recent content in Self-Hosting 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>Sun, 06 Aug 2023 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://carlosvaz.com/tags/self-hosting/index.xml" rel="self" type="application/rss+xml" />
    <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>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>
  </channel>
</rss>
