<?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>Rpi on Haseeb Majid</title>
    <link>https://haseebmajid.dev/tags/rpi/</link>
    <description>Recent content in Rpi on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Wed, 20 Dec 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/tags/rpi/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>TIL: How to Use Tailscale to Connect to K3S PI Cluster</title>
      <link>https://haseebmajid.dev/posts/2023-12-20-til-how-to-use-tailscale-to-connect-to-k3s-pi-cluster/</link>
      <pubDate>Wed, 20 Dec 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-12-20-til-how-to-use-tailscale-to-connect-to-k3s-pi-cluster/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TIL: How to Use Tailscale to Connect to K3S PI Cluster&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So what do we do if want to connect to our K3S cluster running our RPIs, but we are not on the same network/at home.
Well we can look to use a VPN, in this article we will be using &lt;a href=&#34;https://tailscale.com/&#34;&gt;tailscale&lt;/a&gt;. It is super easy
to setup on NixOS, and we need very little config for Tailscale. It also has a generous free tier, which will be
more than enough for our home lab use case.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>TIL: How to Use Tailscale to Connect to K3S PI Cluster</strong></p>
<p>So what do we do if want to connect to our K3S cluster running our RPIs, but we are not on the same network/at home.
Well we can look to use a VPN, in this article we will be using <a href="https://tailscale.com/">tailscale</a>. It is super easy
to setup on NixOS, and we need very little config for Tailscale. It also has a generous free tier, which will be
more than enough for our home lab use case.</p>
<p>So essentially, what we will need to do is have a Tailscale service always running on each of PI hosts, though we
could have it running on our main control node only (called strawberry from me, see previous articles in this series
for more context). Once we start the service we will need to auth with Tailscale the first time, manually though.</p>
<p>The device we want to connect to our cluster from will also need to be running the Tailscale VPN, but we only need it
running when we want to access our cluster.</p>
<p>All we need to do is add a line to our <code>common.nix</code>, this line <code>services.tailscale.enable = true;</code>. We can then
deploy this using Colmena like <code>colmena apply switch --build-on-target</code>. This will deploy tailscale onto each our PI
cluster nodes.</p>
<p>Then to authenticate with Tailscale we can ssh to our node and run the following <code>sudo tailscale up</code>. We can then auth
in the browser by logging in. We can then also see the device on the Tailscale website.</p>
<p><img
        loading="lazy"
        src="/posts/2023-12-20-til-how-to-use-tailscale-to-connect-to-k3s-pi-cluster/images/tailscale.png"
        type=""
        alt="Tailscale GUI"
        
      /></p>
<p>Then where before I might use <code>strawberry.local</code> to connect now I can use <code>strawberry</code> when I am on the Tailscale VPN.
Note if you want self host rather than using Tailscale, there is an open source project called
<a href="https://github.com/juanfont/headscale">headscale</a>.</p>
<p>That&rsquo;s it! We deployed Tailscale and can connect to our PI K3S cluster, from anywhere.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to Deploy K3s With Colmena on Pi Cluster</title>
      <link>https://haseebmajid.dev/posts/2023-12-16-how-to-deploy-k3s-with-colmena-on-pi-cluster/</link>
      <pubDate>Sat, 16 Dec 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-12-16-how-to-deploy-k3s-with-colmena-on-pi-cluster/</guid>
      <description>&lt;p&gt;In this post, we will go over how we can deploy K3S on our PI cluster we have set up. Which is running NixOS,
and we can also pass secrets using sops nix based on the previous parts of this series.&lt;/p&gt;
&lt;p&gt;Some of you maybe wondering what is &lt;a href=&#34;https://k3s.io/&#34;&gt;K3S&lt;/a&gt;, it is a Kubernetes distribution which is tiny i.e.
the binary is only 50 MB. It also has fewer dependencies. Make it perfect our PI cluster and home lab and IoT apps.
I am still going to work out how to manage the Kubernetes cluster itself, perhaps I will use Pulumi you could also
terraform if you wanted a reproducible PI cluster.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this post, we will go over how we can deploy K3S on our PI cluster we have set up. Which is running NixOS,
and we can also pass secrets using sops nix based on the previous parts of this series.</p>
<p>Some of you maybe wondering what is <a href="https://k3s.io/">K3S</a>, it is a Kubernetes distribution which is tiny i.e.
the binary is only 50 MB. It also has fewer dependencies. Make it perfect our PI cluster and home lab and IoT apps.
I am still going to work out how to manage the Kubernetes cluster itself, perhaps I will use Pulumi you could also
terraform if you wanted a reproducible PI cluster.</p>
<p>In our <code>common.nix</code>, let&rsquo;s enable the k3s service which will start k3s for us, one of the nice things about Nix. We
will also add the token that the K3S nodes will need to communicate with each other:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">k3s</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">sops</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">k3s_token</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">sopsFile</span> <span class="o">=</span> <span class="sr">./secrets.yaml</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">k3s</span><span class="o">.</span><span class="n">tokenFile</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">sops</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">k3s_token</span><span class="o">.</span><span class="n">path</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This needs to be secret, so we will use the sops-nix. In my case, I deployed the main node and then took the key
from the node itself and shared it with the agents, so they can join the cluster. However, when we start the main node
we can also pass it a token we specify. Sops saves the token to a file; hence we use <code>tokenFile</code> here, as we cannot
actually access the value in our nix config.</p>
<p>We may also want to update our firewall, and open ports so that the nodes in the cluster can communicate each other
from the hosts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">networking</span><span class="o">.</span><span class="n">firewall</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">allowedTCPPorts</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="mi">22</span>
</span></span><span class="line"><span class="cl">      <span class="mi">6443</span>
</span></span><span class="line"><span class="cl">      <span class="mi">6444</span>
</span></span><span class="line"><span class="cl">      <span class="mi">9000</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>We don&rsquo;t need to do much else for the main node, but for each agent node we need to tell it is that it should act
as an agent, and which hostname to connect to join the nodes. Which we can do something like this <code>mango.nix</code>;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">k3s</span><span class="o">.</span><span class="n">role</span> <span class="o">=</span> <span class="s2">&#34;agent&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">k3s</span><span class="o">.</span><span class="n">serverAddr</span> <span class="o">=</span> <span class="s2">&#34;https://strawberry.local:6443&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Where they are all running in the same local network; hence they can connect using the hostname and <code>.local</code>. This was
avahi service we set up in a previous post.</p>
<p>So in each 3 of my agent nodes this config is copies, so these nodes act as agents and connect to the k8s cluster
correctly. We can then deploy the app as we normally would using Colmena, <code>colmena apply switch --build-on-target</code>.</p>
<p>Then we can follow this <a href="https://docs.k3s.io/cluster-access">tutorial</a> for cluster access using <code>kubectl</code>. We can
then check all the nodes in the cluster by doing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get nodes
</span></span><span class="line"><span class="cl">NAME         STATUS   ROLES                  AGE   VERSION
</span></span><span class="line"><span class="cl">strawberry   Ready    control-plane,master   41d   v1.27.6+k3s1
</span></span><span class="line"><span class="cl">orange       Ready    &lt;none&gt;                 34d   v1.27.6+k3s1
</span></span><span class="line"><span class="cl">mango        Ready    &lt;none&gt;                 32d   v1.27.6+k3s1
</span></span><span class="line"><span class="cl">guava        Ready    &lt;none&gt;                 35d   v1.27.6+k3s1
</span></span></code></pre></div><p>That&rsquo;s it! We successfully deployed k3s to our PI cluster using Colmena and even sops-nix from our previous posts.
In the next post, we will look at how we can add Tailscale to be able to securely connect to our cluster from anywhere.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>TIL: How to Use Sops Nix With Colmena</title>
      <link>https://haseebmajid.dev/posts/2023-11-30-til-how-to-use-sops-nix-with-colmena/</link>
      <pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-11-30-til-how-to-use-sops-nix-with-colmena/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TIL: How to Use Sops Nix With Colmena&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If we are using colmena, how can we set it up when we deploy a secret, for example when deploying k3s the token?
i.e. &lt;code&gt;services.k3s.tokenFile = &amp;quot;/my.token&amp;quot;;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So to do this first, I will assume you already have a colmena config and sops-nix setup in your config.
First, let&amp;rsquo;s set up our hosts, in this case RPIs which already come &lt;code&gt;/etc/ssh/ssh_host_ed25519_key&lt;/code&gt; ssh key we can turn
to an age key, i.e. in our &lt;code&gt;.sops.yaml&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>TIL: How to Use Sops Nix With Colmena</strong></p>
<p>If we are using colmena, how can we set it up when we deploy a secret, for example when deploying k3s the token?
i.e. <code>services.k3s.tokenFile = &quot;/my.token&quot;;</code>.</p>
<p>So to do this first, I will assume you already have a colmena config and sops-nix setup in your config.
First, let&rsquo;s set up our hosts, in this case RPIs which already come <code>/etc/ssh/ssh_host_ed25519_key</code> ssh key we can turn
to an age key, i.e. in our <code>.sops.yaml</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">keys</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="cp">&amp;users:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="cp">&amp;haseeb</span><span class="w"> </span><span class="l">F04F743A24CD81B628A20667CD20E7373D83B71C</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="cp">&amp;hosts:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="cp">&amp;strawberry</span><span class="w"> </span><span class="l">age1qng4kav7deqtjmxeqz2vnyxywaqplf8k2lu3q347r2rz4zxdsynq0sf4um</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="cp">&amp;orange</span><span class="w"> </span><span class="l">age187eesfqwv04gpd2dnfwsjgleevr57v6xvrwujjy8ehhf0ehl338qdnlqlf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="cp">&amp;guava</span><span class="w"> </span><span class="l">age10qsd50v2qmvn4vy4l8cjxvjxjuvedkxjc0a72ap9laap9mz6rctqmp3efl</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="cp">&amp;mango</span><span class="w"> </span><span class="l">age16tskx6gle6v4v0hzhm5fvj0yd29mmn0s47d8q0h3tgcj9wej53uquv98cn</span><span class="w">
</span></span></span></code></pre></div><p>To get this file, we need to log in to our rpi host and run
<code>nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'</code>. Then we add our secrets file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">creation_rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">path_regex</span><span class="p">:</span><span class="w"> </span><span class="l">hosts/rpis/secrets.ya?ml$</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">key_groups</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">age</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="cp">*strawberry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="cp">*orange</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="cp">*guava</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="cp">*mango</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">pgp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="cp">*haseeb</span><span class="w">
</span></span></span></code></pre></div><p>Then we can create our actual secrets file running <code>sops hosts/rpis/secrets.yaml</code>. Now we can reference
these secrets in our colmena config. Let&rsquo;s add sops to our common config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">defaults</span> <span class="o">=</span> <span class="p">{</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="n">inputs</span><span class="o">.</span><span class="n">hardware</span><span class="o">.</span><span class="n">nixosModules</span><span class="o">.</span><span class="n">raspberry-pi-4</span>
</span></span><span class="line hl"><span class="cl">      <span class="n">inputs</span><span class="o">.</span><span class="n">sops-nix</span><span class="o">.</span><span class="n">nixosModules</span><span class="o">.</span><span class="n">sops</span>
</span></span><span class="line"><span class="cl">      <span class="sr">./rpis/common.nix</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>For example, if we take look at <code>common.nix</code> we can use sops like we normally would:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="n">sops</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">k3s_token</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">sopsFile</span> <span class="o">=</span> <span class="sr">./secrets.yaml</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">k3s</span><span class="o">.</span><span class="n">tokenFile</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">sops</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">k3s_token</span><span class="o">.</span><span class="n">path</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">sops</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">age</span><span class="o">.</span><span class="n">sshKeyPaths</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;/etc/ssh/ssh_host_ed25519_key&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then, we can run <code>colmena switch</code> like we usually would. Then the secret is made available at <code>/run/secrets/k3s_token</code>
on the rpis like it normally would be.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How I Manage My Raspberry Pi Cluster Using Colmena</title>
      <link>https://haseebmajid.dev/posts/2023-11-28-how-to-manage-my-raspberry-pi-cluster-using-colmena/</link>
      <pubDate>Tue, 28 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-11-28-how-to-manage-my-raspberry-pi-cluster-using-colmena/</guid>
      <description>&lt;p&gt;So in the previous article I showed you how I had set up my 4 RPI (Raspberry Pi) cluster and put NixOS on the machines.
They are now connectable over SSH using just their hostnames, i.e. &lt;code&gt;ssh strawberry@strawberry.local&lt;/code&gt;. Initially
we deployed NixOS and a basic configuration to each of the RPIs manually.&lt;/p&gt;
&lt;p&gt;We want to automate this process rather than deploying to each machine manually. I looked at
&lt;a href=&#34;https://github.com/rapenne-s/bento/&#34;&gt;bento&lt;/a&gt;, but couldn&amp;rsquo;t quite work out how to make it work for my use case.
Then I found &lt;a href=&#34;https://github.com/zhaofengli/colmena&#34;&gt;colmena&lt;/a&gt;, which worked (is working) to do what I needed.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>So in the previous article I showed you how I had set up my 4 RPI (Raspberry Pi) cluster and put NixOS on the machines.
They are now connectable over SSH using just their hostnames, i.e. <code>ssh strawberry@strawberry.local</code>. Initially
we deployed NixOS and a basic configuration to each of the RPIs manually.</p>
<p>We want to automate this process rather than deploying to each machine manually. I looked at
<a href="https://github.com/rapenne-s/bento/">bento</a>, but couldn&rsquo;t quite work out how to make it work for my use case.
Then I found <a href="https://github.com/zhaofengli/colmena">colmena</a>, which worked (is working) to do what I needed.</p>
<h2 id="what-does-it-do">What does it do?</h2>
<p>We push changes from our Desktop and deploy to all our rpis at once, without needing to deploy to each manually.
Using one command, <code>colmena apply switch --build-on-target</code> It will deploy our changes to all our RPIs. Where we
specify common config shared by all the machines, but then also specific config for each of our RPIs.</p>
<h2 id="setup">Setup</h2>
<p>Let&rsquo;s have a look at how we can set up Colmena, so, in our current Nix flake config. Add colmena as an input:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">inputs</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">colmena</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:zhaofengli/colmena&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then in the outputs, let use specify a few things:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">outputs</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="n">self</span>
</span></span><span class="line"><span class="cl">    <span class="o">,</span> <span class="n">nixpkgs</span>
</span></span><span class="line"><span class="cl">    <span class="o">,</span> <span class="n">colmena</span>
</span></span><span class="line"><span class="cl">    <span class="o">,</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="o">@</span> <span class="n">inputs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">colmena</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">meta</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">nixpkgs</span> <span class="o">=</span> <span class="kn">import</span> <span class="n">nixpkgs</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">system</span> <span class="o">=</span> <span class="s2">&#34;x86_64-linux&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="p">};</span>
</span></span><span class="line"><span class="cl">          <span class="n">specialArgs</span> <span class="o">=</span> <span class="n">inputs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">defaults</span> <span class="o">=</span> <span class="p">{</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="n">inputs</span><span class="o">.</span><span class="n">hardware</span><span class="o">.</span><span class="n">nixosModules</span><span class="o">.</span><span class="n">raspberry-pi-4</span>
</span></span><span class="line"><span class="cl">            <span class="sr">./hosts/rpis/common.nix</span>
</span></span><span class="line"><span class="cl">          <span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">strawberry</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="sr">./hosts/rpis/strawberry.nix</span>
</span></span><span class="line"><span class="cl">          <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">nixpkgs</span><span class="o">.</span><span class="n">system</span> <span class="o">=</span> <span class="s2">&#34;aarch64-linux&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">deployment</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">buildOnTarget</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">targetHost</span> <span class="o">=</span> <span class="s2">&#34;strawberry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">targetUser</span> <span class="o">=</span> <span class="s2">&#34;strawberry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">tags</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;rpi&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">          <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Breaking this file down, first we create a section for colmena, then we specify some meta information i.e.
which nixpkgs to use. Then one of the cool bits of colmena we can specify some common config between all of our hosts.</p>
<p>Here we are importing sops-nix for secret management and hardware module for Raspberry Pi 4, so we can get various
optimised settings.</p>
<h3 id="commonnix">common.nix</h3>
<p>Our <code>common.nix</code> looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">config</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">boot</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kernelPackages</span> <span class="o">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">linuxKernel</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="n">linux_rpi4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">kernelParams</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cgroup_memory=1&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cgroup_enable=cpuset&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cgroup_enable=memory&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">initrd</span><span class="o">.</span><span class="n">availableKernelModules</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;xhci_pci&#34;</span> <span class="s2">&#34;usbhid&#34;</span> <span class="s2">&#34;usb_storage&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">loader</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">grub</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">generic-extlinux-compatible</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">fileSystems</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;/&#34;</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">device</span> <span class="o">=</span> <span class="s2">&#34;/dev/disk/by-label/NIXOS_SD&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">fsType</span> <span class="o">=</span> <span class="s2">&#34;ext4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;noatime&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">networking</span><span class="o">.</span><span class="n">firewall</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">allowedTCPPorts</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="mi">22</span>
</span></span><span class="line"><span class="cl">      <span class="mi">6443</span>
</span></span><span class="line"><span class="cl">      <span class="mi">6444</span>
</span></span><span class="line"><span class="cl">      <span class="mi">9000</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">programs</span><span class="o">.</span><span class="n">fish</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">users</span><span class="o">.</span><span class="n">users</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">hashedPassword</span> <span class="o">=</span> <span class="s2">&#34;!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">environment</span><span class="o">.</span><span class="n">systemPackages</span> <span class="o">=</span> <span class="k">with</span> <span class="n">pkgs</span><span class="p">;</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="n">git</span>
</span></span><span class="line"><span class="cl">    <span class="n">vim</span>
</span></span><span class="line"><span class="cl">    <span class="n">wget</span>
</span></span><span class="line"><span class="cl">    <span class="n">curl</span>
</span></span><span class="line"><span class="cl">    <span class="n">gnupg</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">avahi</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">nssmdns</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">addresses</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">domain</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">hinfo</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">userServices</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">workstation</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">openssh</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">settings</span><span class="o">.</span><span class="n">PasswordAuthentication</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">settings</span><span class="o">.</span><span class="n">KbdInteractiveAuthentication</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">security</span><span class="o">.</span><span class="n">sudo</span><span class="o">.</span><span class="n">wheelNeedsPassword</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">hardware</span><span class="o">.</span><span class="n">enableRedistributableFirmware</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">system</span><span class="o">.</span><span class="n">stateVersion</span> <span class="o">=</span> <span class="s2">&#34;23.11&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This config will be applied to all of our hosts. It includes things like setting up ssh, installing some packages
and setting up a default shell (fish). Though specifics don&rsquo;t matter much, more just you can put common config
into a nix module.</p>
<h3 id="host-specific-config">Host-Specific Config</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="n">strawberry</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="sr">./hosts/rpis/strawberry.nix</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">nixpkgs</span><span class="o">.</span><span class="n">system</span> <span class="o">=</span> <span class="s2">&#34;aarch64-linux&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">deployment</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">buildOnTarget</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">targetHost</span> <span class="o">=</span> <span class="s2">&#34;strawberry.local&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">targetUser</span> <span class="o">=</span> <span class="s2">&#34;strawberry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">tags</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;rpi&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>We import nix specific config for our host, in this case the host name of the machine is strawberry and gave the same
The name of the colmena &ldquo;resource&rdquo;. Where my <code>strawberry.nix</code> file looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">config</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="n">lib</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">let</span>
</span></span><span class="line"><span class="cl">  <span class="n">hostname</span> <span class="o">=</span> <span class="s2">&#34;strawberry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">in</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">networking</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">hostName</span> <span class="o">=</span> <span class="n">hostname</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">nix</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">trusted-users</span> <span class="o">=</span> <span class="p">[</span> <span class="n">hostname</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">users</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">users</span><span class="o">.</span><span class="s2">&#34;</span><span class="si">${</span><span class="n">hostname</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">isNormalUser</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">shell</span> <span class="o">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">fish</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">extraGroups</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;wheel&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">password</span> <span class="o">=</span> <span class="n">hostname</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">openssh</span><span class="o">.</span><span class="n">authorizedKeys</span><span class="o">.</span><span class="n">keys</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxe8kDCJa6xcAM9WE8c5amGG+2secXmnof7vlmAq1Da hello@haseebmajid.dev&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Which just includes some specific config like hostname and username, and other ssh keys that can be used to log in to
this user. Again, the specifics don&rsquo;t matter too much, it&rsquo;s more the idea we can have specific config for our strawberry
host.</p>
<p>Then going over the rest of our config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="n">strawberry</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="sr">./hosts/rpis/strawberry.nix</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">  <span class="n">nixpkgs</span><span class="o">.</span><span class="n">system</span> <span class="o">=</span> <span class="s2">&#34;aarch64-linux&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">deployment</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">buildOnTarget</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">targetHost</span> <span class="o">=</span> <span class="s2">&#34;strawberry.local&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">targetUser</span> <span class="o">=</span> <span class="s2">&#34;strawberry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">tags</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;rpi&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This specifies things like the host to connect to, i.e. <code>strawberry.local</code> and the user to log in with <code>strawberry</code>.
We can also add <code>tags</code> which we can use during deployment to deploy to machines with specific tags.</p>
<p>Then we simply specify our remaining hosts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">colmena</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">orange</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="sr">./hosts/rpis/orange.nix</span>
</span></span><span class="line"><span class="cl">      <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">nixpkgs</span><span class="o">.</span><span class="n">system</span> <span class="o">=</span> <span class="s2">&#34;aarch64-linux&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">deployment</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">buildOnTarget</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">targetHost</span> <span class="o">=</span> <span class="s2">&#34;orange.local&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">targetUser</span> <span class="o">=</span> <span class="s2">&#34;orange&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tags</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;rpi&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># other hosts ...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p>If you set deployment.buildOnTarget = true; for a node, then the actual build process will be initiated on the node itself. Colmena will evaluate the configuration locally before copying the derivations to the target node.</p>
</blockquote>
<p>We want to build the nix config on the pis, rather than on my desktop, as they are different architectures, x86 vs arch.</p>
<h2 id="deploy">Deploy</h2>
<p>Now to deploy to our rpis we can do <code>colmena switch</code>, from my desktop. Where my desktop has connectivity
to all my PIs (running on the same local network). That should be it, as long as we can connect to the PIs from our
machine we run the colmena command on it will deploy the new config.</p>
<p>In the next article, we will look at how we can manage secrets using sops-nix when deploying using colmena.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/dotfiles/-/blob/d8aefb2b2dbd468b221f2b6074994a87761ae981/hosts/self-hosted/colmena.nix">My colmena config</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How I Setup My Raspberry Pi Cluster With Nixos</title>
      <link>https://haseebmajid.dev/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/</link>
      <pubDate>Sat, 18 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;Recently, I proceeded to experiment with some Raspberry PIs (RPI) that I had lying around. I wanted to do something
with them, so I decided I would turn them into a k8s cluster and put various random tools that might be nice to have
on it. Such as a GitLab runner, Jellyfin media server &amp;amp; pi hole for ad blocking.&lt;/p&gt;
&lt;h2 id=&#34;hardware&#34;&gt;Hardware&lt;/h2&gt;
&lt;p&gt;The list below shows the things I used to set up my rpi cluster. None of this is sponsored! I paid for everything
listed below:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>Recently, I proceeded to experiment with some Raspberry PIs (RPI) that I had lying around. I wanted to do something
with them, so I decided I would turn them into a k8s cluster and put various random tools that might be nice to have
on it. Such as a GitLab runner, Jellyfin media server &amp; pi hole for ad blocking.</p>
<h2 id="hardware">Hardware</h2>
<p>The list below shows the things I used to set up my rpi cluster. None of this is sponsored! I paid for everything
listed below:</p>
<p><img
        loading="lazy"
        src="/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/images/pi-cluster-front.jpeg"
        type=""
        alt="Pi Cluster Front"
        
      />
<img
        loading="lazy"
        src="/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/images/pi-cluster-side.jpeg"
        type=""
        alt="Pi Cluster Side"
        
      /></p>
<ul>
<li>Utronics SSD Cluster Case for Raspberry Pi
<ul>
<li>SKU: U6244</li>
</ul>
</li>
<li>2x Raspberry Pi 4 - 4GB RAM</li>
<li>2x Raspberry Pi 4 - 8GB RAM</li>
<li>2x 2TB SSD</li>
<li>2x 2TB Hard Disks 2.5&quot;</li>
<li>4x SSD to USB 3.0 Cable for Raspberry Pi</li>
<li>4x Raspberry Pi PoE+ HAT</li>
<li>4x MicroSD Extender Set for Uctronics Cluster Cases
<ul>
<li>Easier to access SD cards in the case</li>
</ul>
</li>
<li>4x 16 GB Micro SD Cards</li>
<li>4x CAT7 Ethernet Cables 0.5M
<ul>
<li>Could&rsquo;ve gone a bit smaller (you can see in the photos)</li>
</ul>
</li>
<li>Powerline Adapter
<ul>
<li>P-Link TL-WPA4220</li>
</ul>
</li>
<li>TP-Link 5-Port Gigabit Desktop PoE Switch
<ul>
<li>TL-SG1005P</li>
</ul>
</li>
<li>Double sided Velcro
<ul>
<li>Attaches switch to Utronics case</li>
</ul>
</li>
</ul>
<p>Extra:</p>
<ul>
<li>Micro SD card to USB Adapter</li>
<li>For flashing the image with NixOS</li>
</ul>
<h2 id="setup">Setup</h2>
<h3 id="power-over-ethernet">Power Over Ethernet</h3>
<p>So I learnt I could power my rpis using Ethernet, power over Ethernet (PoE). This saves us needing an extra cable on each
pi, I had a USB hub on top of my pi cluster (on top of the switch).</p>
<p>To use POE, we need to have a switch which can be used for POE, we also need a PoE hat on the Raspberry Pi. Then finally
an Ethernet cable from the switch to the Raspberry Pi. We still need to power the switch from our mains. Since my rpis
will be away from my router, I also use a powerline adapter for a more stable internet connection. So 4 of the ports
in the switch connects to the rpis and the 5th connects to a powerline adapter.</p>
<p><img
        loading="lazy"
        src="/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/images/ethernet-hat.jpeg"
        type=""
        alt="Ethernet Hat"
        
      /></p>
<h3 id="ssd-to-usb">SSD to USB</h3>
<p>Since the SD cards I got only are 16GB for more persistent storage, I used some of my spares SSDs and hard disks.
Once nice thing was the case has a nice slot for the SSDs to be placed (under the pis).</p>
<p><img
        loading="lazy"
        src="/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/images/side-view.jpeg"
        type=""
        alt="Side View"
        
      /></p>
<p>Then we use an adapter to convert the SSDs to USB 3.0 on the pi. So we can still access the SSD, with the PI. For
more persistent data storage.</p>
<p><img
        loading="lazy"
        src="/posts/2023-11-18-how-i-setup-my-raspberry-pi-cluster-with-nixos/images/ssd-to-usb.jpeg"
        type=""
        alt="SSD to USB"
        
      /></p>
<h3 id="install-nixos">Install NixOS</h3>
<p>I followed this <a href="https://nix.dev/tutorials/nixos/installing-nixos-on-a-raspberry-pi">tutorial</a>, to setup NixOS on
my pi cluster. We will need to repeat this process for each of pis (4x).</p>
<p>Assuming our host is running nix we can do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="n">nix-shell</span> <span class="err">-</span><span class="n">p</span> <span class="n">wget</span> <span class="n">zstd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">wget</span> <span class="sd">https://hydra.nixos.org/build/226381178/download/1/nixos-sd-image-23.11pre541036.63678e9f3d3a-aarch64-linux.img.zst</span>
</span></span><span class="line"><span class="cl"><span class="n">unzstd</span> <span class="err">-</span><span class="n">d</span> <span class="n">nixos-sd-image-23</span><span class="o">.</span><span class="err">11</span><span class="n">pre500597</span><span class="o">.</span><span class="err">0</span><span class="n">fbe93c5a7c-aarch64-linux</span><span class="o">.</span><span class="n">img</span><span class="o">.</span><span class="n">zst</span>
</span></span><span class="line"><span class="cl"><span class="n">dmesg</span> <span class="err">--</span><span class="n">follow</span>
</span></span></code></pre></div><p>Plug in your SD card and your terminal should print what device it got assigned, for example /dev/sdX.
Press Ctrl+C to stop dmesg &ndash;follow.</p>
<p>Then finally we can do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="n">sudo</span> <span class="n">dd</span> <span class="k">if</span><span class="err">=</span><span class="n">nixos-sd-image-23</span><span class="o">.</span><span class="err">11</span><span class="n">pre541036</span><span class="o">.</span><span class="err">63678</span><span class="n">e9f3d3a-aarch64-linux</span><span class="o">.</span><span class="n">img</span> <span class="n">of</span><span class="err">=</span><span class="sr">/dev/sda</span> <span class="n">bs</span><span class="err">=</span><span class="mi">4096</span> <span class="n">conv</span><span class="err">=</span><span class="n">fsync</span> <span class="n">status</span><span class="err">=</span><span class="n">progres</span>
</span></span></code></pre></div><p>Then put the SD card in your pi. Before we can start deploying to them
we need to get some bare-bones setup we can SSH to the machine.</p>
<p>Then I connect the PI to a keyboard and monitor, so I can see the IP address and set a default password on the <code>nixos</code>
user.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="n">passwd</span> <span class="n">nixos</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#123456</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ifconfig</span>
</span></span></code></pre></div><p>Then we can ssh to the pi from the same network, i.e. my main NixOS machine. By doing something like:</p>
<p><code>ssh nixos@192.168.0.75</code>, replace 192.168.0.75 with the IP address of your pi, returned from the ifconfig command.
Then create a new config file:</p>
<h3 id="ssh">ssh</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nix-shell -p git vim
</span></span><span class="line"><span class="cl">sudo vim /etc/nixos/configuration.nix
</span></span></code></pre></div><p>With the following contents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"> <span class="p">{</span><span class="n">config</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="n">lib</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">let</span>
</span></span><span class="line"><span class="cl">  <span class="n">hostname</span> <span class="o">=</span> <span class="s2">&#34;strawberry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">in</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;</span><span class="si">${</span><span class="nb">builtins</span><span class="o">.</span><span class="n">fetchGit</span> <span class="p">{</span> <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;https://github.com/NixOS/nixos-hardware.git&#34;</span><span class="p">;</span> <span class="p">}</span><span class="si">}</span><span class="s2">/raspberry-pi/4&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">boot</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kernelPackages</span> <span class="o">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">linuxKernel</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="n">linux_rpi4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">initrd</span><span class="o">.</span><span class="n">availableKernelModules</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;xhci_pci&#34;</span> <span class="s2">&#34;usbhid&#34;</span> <span class="s2">&#34;usb_storage&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">loader</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">grub</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">generic-extlinux-compatible</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">fileSystems</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;/&#34;</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">device</span> <span class="o">=</span> <span class="s2">&#34;/dev/disk/by-label/NIXOS_SD&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">fsType</span> <span class="o">=</span> <span class="s2">&#34;ext4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;noatime&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">networking</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">hostName</span> <span class="o">=</span> <span class="n">hostname</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">environment</span><span class="o">.</span><span class="n">systemPackages</span> <span class="o">=</span> <span class="k">with</span> <span class="n">pkgs</span><span class="p">;</span> <span class="p">[</span> <span class="n">git</span> <span class="n">vim</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="n">nix</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">trusted-users</span> <span class="o">=</span> <span class="p">[</span> <span class="n">hostname</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">openssh</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">settings</span><span class="o">.</span><span class="n">PasswordAuthentication</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">settings</span><span class="o">.</span><span class="n">KbdInteractiveAuthentication</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">users</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">users</span><span class="o">.</span><span class="s2">&#34;</span><span class="si">${</span><span class="n">hostname</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">isNormalUser</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">extraGroups</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;wheel&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># Note: This is not very secure, I only use this password for a little while whilst the PI </span>
</span></span><span class="line"><span class="cl">      <span class="c1"># can only be accessible from my local network</span>
</span></span><span class="line"><span class="cl">      <span class="n">password</span> <span class="o">=</span> <span class="n">hostname</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">openssh</span><span class="o">.</span><span class="n">authorizedKeys</span><span class="o">.</span><span class="n">keys</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&#34;ssh-ed25519 &lt;YOUR_PUB_KEY&gt;&#34;</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="n">security</span><span class="o">.</span><span class="n">sudo</span><span class="o">.</span><span class="n">wheelNeedsPassword</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">services</span><span class="o">.</span><span class="n">avahi</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">nssmdns</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">addresses</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">domain</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">hinfo</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">userServices</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">workstation</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">hardware</span><span class="o">.</span><span class="n">enableRedistributableFirmware</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">system</span><span class="o">.</span><span class="n">stateVersion</span> <span class="o">=</span> <span class="s2">&#34;23.11&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Finally, <code>sudo nixos-rebuild switch</code> to build this new configuration.</p>
<p>This setups a few things on my machine, with the <code>avahi</code> service, we can connect to the pi using the host name instead of
the IP address. This saves needing to create a static IP for each pi. So I can now connect to this pi like:</p>
<p><code>ssh strawberry@strawberry.local</code></p>
<p>We also don&rsquo;t need a password as I can use my ssh key to log in</p>
<h4 id="firmware-update">Firmware Update</h4>
<p>If we want to update our pi firmware, we can do something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nix-shell -p raspberrypi-eeprom
</span></span><span class="line"><span class="cl">mount /dev/disk/by-label/FIRMWARE /mnt
</span></span><span class="line"><span class="cl"><span class="nv">BOOTFS</span><span class="o">=</span>/mnt <span class="nv">FIRMWARE_RELEASE_STATUS</span><span class="o">=</span>stable rpi-eeprom-update -d -a
</span></span></code></pre></div><p>That&rsquo;s It! We&rsquo;ve set up our rpi cluster with NixOS, and we can connect to it using the hostname. In the next couple of
tutorials we will look at how we can use Nix tools to manage the deployment and deploy k3s to the cluster.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
