<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Pyce&apos;s Blog</title><description>Pyce&apos;s thoughts or something like that.</description><link>https://blog.pyce.eu</link><item><title>My Homelab</title><link>https://blog.pyce.eu/posts/homelab</link><guid isPermaLink="true">https://blog.pyce.eu/posts/homelab</guid><description>What my homelab infrastructure looks like.</description><pubDate>Mon, 27 Oct 2025 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;I have many different services I want to deploy and have full control over. Privacy is becoming more important in a world
where we are having less of it day by day. Open-source, self-hostable software mitigate this problem, and they are awesome!
Whether that would be a password manager or a VPN, there needs to be a computer to host these various applications.
Luckily, I have a spare Raspberry Pi 5 (RPi 5) at home, a cheap Virtual Private Server (VPS) rented from Hetzner, and a domain from INWX.
I will go through my homelab and how it&apos;s set up.&lt;/p&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./Architecture%202026-02-18.png&quot; alt=&quot;Architecture of the homelab.&quot; title=&quot;A simplified overview of the architecture.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In principle, the homelab is very simple. Each service hosted on the VPS and RPi 5 is routed to via Caddy.
Each service is a Docker &lt;code&gt;compose.yaml&lt;/code&gt; file which contains the configurations for each application,
including the frontend and all backend services such as databases.
To self-host a new application, it&apos;s as simple as creating a &lt;code&gt;compose.yaml&lt;/code&gt; file,
running &lt;code&gt;docker compose up -d&lt;/code&gt;, and editing the Caddy configuration!&lt;/p&gt;
&lt;h3&gt;Reverse Proxying - Caddy&lt;/h3&gt;
&lt;p&gt;For reverse proxying to be possible, all of the services expose a Docker network
which are accessible by Caddy through declaring external networks which are attached to the Caddy container.
This enables Caddy to serve each route and subdomain to the correct service in a very simple way.
Even receiving emails work with Caddy!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;&amp;lt;i&amp;gt;caddyfile&amp;lt;/i&amp;gt;&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;In this example, &lt;strong&gt;forgejo&lt;/strong&gt; is a container which exposed its network to Caddy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;forgejo.pyce.eu {
  reverse_proxy forgejo:3000
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;You may have noticed that to access the portfolio (&quot;&lt;a href=&quot;https://portfolio.pyce.eu&quot;&gt;portfolio.pyce.eu&lt;/a&gt;&quot;), you&apos;re routed first to &lt;a href=&quot;https://anubis.techaro.lol/&quot;&gt;Anubis&lt;/a&gt;, and then my portfolio page. Why? It&apos;s because
I do not want bots and AI scraping my portfolio, and therefore block those requests with the help of Anubis.
This is possible by reverse proxying to the Anubis instance first, and when it has confirmed that the visitor
is not a bot or scraper, it redirects to the portfolio!&lt;/p&gt;
&lt;h3&gt;The VPN - Headscale&lt;/h3&gt;
&lt;p&gt;I figured that since I own a VPS, I may as well install a VPN and self-host it.
The best one in terms of Docker support and ease of access I found was &lt;a href=&quot;https://headscale.net/stable/&quot;&gt;Headscale&lt;/a&gt;, and it&apos;s excellent!
The setup is very simple and the VPN runs great, although it lacks a web UI. Luckily, &lt;a href=&quot;https://github.com/tale/headplane&quot;&gt;Headplane&lt;/a&gt; saves the day!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;&amp;lt;i&amp;gt;compose.yaml&amp;lt;/i&amp;gt;&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    restart: unless-stopped
    expose:
      - 8080
      # - 9090
    volumes:
      - ./headscale-config:/etc/headscale
      - headscale_data_lib:/var/lib/headscale
      - headscale_data_run:/var/run/headscale
    labels:
      me.tale.headplane.target: headscale
    command: serve
    networks:
      - headscale_network

  headplane:
    image: ghcr.io/tale/headplane:latest
    container_name: headplane
    restart: unless-stopped
    volumes:
      - ./headplane-config/config.yaml:/etc/headplane/config.yaml
      - ./headscale-config/config.yaml:/etc/headscale/config.yaml
      - headplane_data:/var/lib/headplane
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - headscale_network

volumes:
  headscale_data_lib:
  headscale_data_run:
  headplane_data:

networks:
  headscale_network:
    # name: headscale_network
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;The code above is all that&apos;s needed to self-host Headscale with a modern web UI! In terms of
reverse proxy support with Caddy, the actual VPN is easy to support, but the website
&lt;a href=&quot;https://headplane.net/install/docker#example-traefik-configuration&quot;&gt;requires more configuration to get it up and running behind a reverse proxy&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Accessing Services&lt;/h3&gt;
&lt;p&gt;A great benefit in terms of security of a VPN is that you can allow access to certains devices only through it.
For example, I have two &lt;a href=&quot;https://github.com/pi-hole/pi-hole&quot;&gt;Pi-hole&lt;/a&gt; DNS servers set up: one on the VPS and the RPi 5 for blocking ads, which all devices use as their DNS,
so I can analyse the traffic and reduce the quantity of wasteful outbound queries.
To access the Pi-hole website, I created an ACL configuration which only allows me to view it through
the VPS&apos; MagicDNS domain name on port 8443:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hosts&quot;: {
    &quot;host-rpi-5&quot;: &quot;100.64.0.5/32&quot;,
    &quot;host-vps&quot;: &quot;100.64.0.8/32&quot;
  },
  &quot;acls&quot;: [
    /*
      Allow everyone in the Headscale network to access:
        - the internet on any exit node.
        - Pi-hole DNS connections.
        - their own devices on all ports.
    */
    {
      &quot;action&quot;: &quot;accept&quot;,
      &quot;src&quot;: [&quot;autogroup:member&quot;],
      &quot;dst&quot;: [&quot;autogroup:internet:*&quot;, &quot;autogroup:self:*&quot;, &quot;host-vps:53&quot;, &quot;host-rpi-5:53&quot;]
    },
    // Different services accessible by Pyce (administrator).
    {
      &quot;action&quot;: &quot;accept&quot;,
      &quot;src&quot;: [&quot;Pyce@&quot;],
      &quot;dst&quot;: [&quot;host-vps:8443&quot;]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition, this level of control is also very useful to determine who can SSH to what device.
I created a similar type of configuration for SSH, so only I can SSH to my own devices on the Virtual Private Network.&lt;/p&gt;
&lt;p&gt;Personally, I access e.g. my Immich instance via a custom DNS record like &lt;code&gt;https://immich.vpn&lt;/code&gt; to avoid having to use the MagicDNS domain and port of the device (e.g. &lt;code&gt;https://rpi-5.home.vp:2283&lt;/code&gt; 🤮). Sure, you get certificate issues on browsers and phones, but it only takes two clicks to ignore the warning, plus you can install the local Caddy certificate to avoid this annoyance; this approach works well on iOS!&lt;/p&gt;
&lt;h3&gt;What to Host Where?&lt;/h3&gt;
&lt;p&gt;You may wonder: how do you decide what service to host on the VPS or Raspberry Pi 5 in your homelab?
It&apos;s rather simple, the services which may require &lt;strong&gt;critical and important&lt;/strong&gt; storage or are planned for personal usage, are hosted at home.
Applications which need lots of uptime and may be used by other people are thus hosted on the VPS.
That&apos;s why services like Ticketer reside at home: the data is important and used by many individuals;
the same goes for Vaultwarden. Other services like Headscale and the mail server need the consistent uptime,
which is guaranteed by the VPS. Just look at &lt;a href=&quot;https://status.pyce.eu&quot;&gt;status.pyce.eu&lt;/a&gt; for the proof of consistent uptime!&lt;/p&gt;
&lt;h2&gt;Monthly Costs&lt;/h2&gt;
&lt;p&gt;What does this setup cost? I will only discuss the monthly payments required and will therefore disregard
the Raspberry Pi 5&apos;s price.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INWX&lt;/strong&gt; (.eu domain)&lt;/td&gt;
&lt;td&gt;€10.63/year ≈ €0.89/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hetzner&lt;/strong&gt; (CAX11 VPS)&lt;/td&gt;
&lt;td&gt;~€4.74/month~ €5.61/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;~≈ €5.63/month~ ≈ €6.5/month = €78/year&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Not bad at all! Let&apos;s compare it to common subscriptions in life:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subscription&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gym&lt;/td&gt;
&lt;td&gt;€35/month = €420/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netflix (Basic)&lt;/td&gt;
&lt;td&gt;€9.49/month ≈ €113.88/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spotify&lt;/td&gt;
&lt;td&gt;€11.80/month = €141.6/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YouTube Premium&lt;/td&gt;
&lt;td&gt;€12.99/month = €155.88/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mullvad VPN&lt;/td&gt;
&lt;td&gt;€5/month = €60/year&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Considering you can utilise the VPS to do whatever you&apos;d like, such as stream media or use it as a VPN,
I&apos;d say the price of a VPS (+ domain) is well worth it! Even if you don&apos;t want to purchase a domain,
you can utilise Dynamic DNS providers to effectively get a free (yet ugly) domain to use.
Even better, if you only plan to host a few services, a simple mini-PC will last you a long time!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With the exquisite assistance of Docker and Caddy, it&apos;s possible to set up many services,
such as a VPN and website, for you and others to use. The VPN allow private access to applications.
The addition of a domain enables setting up a mail server which you can use for regular email communication.
Finally, it&apos;s cheap[^1] to rent a VPS and a domain,
or use your own spare computer at home to accomplish what I and many others have done!&lt;/p&gt;
&lt;p&gt;[^1]: I consider it cheap, but others may not have the same privilege to call it inexpensive.&lt;/p&gt;
</content:encoded><author>Pyce</author></item><item><title>Hur man fuskar NOKflex</title><link>https://blog.pyce.eu/posts/fuska-nokflex</link><guid isPermaLink="true">https://blog.pyce.eu/posts/fuska-nokflex</guid><description>Snabb guide att få rätt på varje matteuppgift.</description><pubDate>Sat, 07 Mar 2026 23:43:00 GMT</pubDate><content:encoded>&lt;p&gt;Om du hatar att se gul eller till och med röd på dina klarade övningar, denna guide är för dig!&lt;/p&gt;
&lt;h2&gt;Instruktioner (på Firefox)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Gå till uppgiften du vill hitta lösningen till.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./uppgift.png&quot; alt=&quot;NOKflex uppgiften.&quot; title=&quot;Bild på själva NOKflex uppgiften.&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Öppna &quot;Developer Tools&quot; eller s.k. &quot;Inspect Element&quot; genom att antingen klicka på F12, Ctrl + Shift + I, eller högerklicka och klicka på &quot;Inspect&quot;. Sedan klicka på &quot;Network&quot; fliken.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./network.png&quot; alt=&quot;Network fliken.&quot; title=&quot;Bild på Network fliken i Developer Tools.&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ladda om NOKflex. Du bör se många nya rader på fliken. Hitta den som har &lt;code&gt;subpart?assignmentId=...&lt;/code&gt; i namn med &lt;code&gt;GET&lt;/code&gt; som metod. &lt;strong&gt;Kom ihåg nummret som ges av &lt;code&gt;current_assignment_id&lt;/code&gt;!&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./subpart.png&quot; alt=&quot;Subpart info.&quot; title=&quot;Bild på Subpart detaljer i Developer Tools.&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Öppna &lt;code&gt;subpart&lt;/code&gt; listen och klicka på &lt;code&gt;assignments&lt;/code&gt;. Hitta elementet som har den rätta &lt;code&gt;assignmentId&lt;/code&gt; enligt &lt;code&gt;current_assignment_id&lt;/code&gt; i instruktionen ovanför. Öppna den, och sedan skrolla ner till &lt;code&gt;solutions&lt;/code&gt; och öppna den. &lt;strong&gt;Där står lösningen!&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./assignment.png&quot; alt=&quot;Assignment listen.&quot; title=&quot;Bild på de olika uppgifterna i Developer Tools.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./l%C3%B6sning.png&quot; alt=&quot;Lösningen till övningen.&quot; title=&quot;Bild på lösningen till övningen enligt Developer Tools.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Om det är svårt att förstå vad som står i svaret, kopiera hela svaret och använd koden nedan i t.ex. &lt;a href=&quot;https://jsfiddle.net&quot;&gt;JSFiddle&lt;/a&gt; för att läsa den på riktigt matematiskt språk. Ändra på &lt;code&gt;answers&lt;/code&gt; konstanten till det du kopierade.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;answer&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;const answers = KOPIERA SVARET HÄR;

document.getElementById(&quot;answer&quot;).innerHTML = answers[0].text;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./kod.png&quot; alt=&quot;Lättläst svar&quot; title=&quot;Bild på ett lättare svar att läsa.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./motsvarande.png&quot; alt=&quot;Motsvarande lösning&quot; title=&quot;Bild på motsvarande lösning på NOKflex.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Varför fungerar det?&lt;/h2&gt;
&lt;p&gt;NOKflex är client-side rendered, vilket innebär att allt innehåll laddas på din webbläsare i stället för NOKflex servrarna. De valde att ha alla lösningar till avsnittet i förväg på din webbläsare för någon anledning, därför har vi tillgång till dessa lösningar.&lt;/p&gt;
</content:encoded><author>Pyce</author></item></channel></rss>