New Raspberry Pi Server

When I upgrade my desktop PC I tradtionally retire my old server and use the replaced PC in its place. Also taking the opportunity for a clean install of Ubuntu server. I recently realised that the machine currently serving that role is overkill for what I need, namely:

A large desktop machine with 6GB RAM and a big PSU is unnecessary.

I switched to a Raspberry Pi 4 with 4GB RAM, got a little cooling fan for it and set up a simple Ubuntu server. The Pi4 has gigabit ethernet and USB3 support. For files I purchased a 4GB WD Passport drive that can be powered from the Pi’s USB3 port. I only have about 2GB of data right now, so room to grow.

Backing this up to two 2GB external drives became tiresome and wasn’t happening as often as it should. I purchased a second 4GB Passport drive planning to rsync nightly rather than use software RAID. Unfortuntely there just wasn’t enough power from the Pi4 to support two drives.

I realised this was a blessing in disguise. Installing Ubuntu server on a Raspberry Pi and plugging the second 4GB drive in to that would give me the nightly rsync backup I need. The 100mb speed of the Pi3’s NIC and USB2 would be slow in comparison, but more than enough for a nightly backup run. Not to mention the peace of mind in knowing if the Pi4 went up in smoke it could take all my data and I’d still have a working backup.

This also meant I could spread the resource load. I moved the PiHole over the Pi3 as it’s only DNS traffic. I’m also using it to serve a few old, separately-powered external drives over the network. I can use the Pi3 to try things out and easily rebuild in case I screw up without taking my main fileserver offline.

I had a spare five-port gigabit network switch that I dedicated to use with this little server array, keeping it all in a plastic box.

While technically this met my needs of emulating my old server’s abilities, as well as cutting power consumption, it was inelegant. Three power-bricks, and two long USB cables to bridge a gap mere inches in length.

I checked the power-consumption of the switch, which turned out to be 5v 0.7a. The Pi4 needs a good 3a, and the Pi3 2.5a, both 5v. That’s about 6.2 amps for the whole thing.

I hopped on one of those Chinese online stores and ordered a 12v 8a power brick, a short micro-usb cable to power the pi3, and a short USB-C cable to power the pi4.

To split the 12v and get it to three 5v devices I wired a connector block in parallel to three 12v-5v buck converters.

With two 30cm cat5e cables I now have a single 12v power brick and a single network cable running the whole thing. I also have two spare network ports, and about 1.8 amps of power going begging.

Maybe I’ll put a Raspberry Pi Zero (or similar SBC), or maybe an ESP32 in there and set up some LoRa.

In hindsight, I could’ve used a 5v power supply (with enough current), but I hadn’t finalised the setup and wasn’t sure everything I might want to power would be 5v. As it stands, I’m thinking of hooking up an old 12v case fan to move some air around everything.

Local Development With Subdomains on WSL2

I’m writing a webapp that loads content based on the subdomain. Doing this locally is problematic because you can’t use a subdomain with http://localhos:8080.

I’m using WSL2 on Windows 10 and can therefore leverage the hosts file, usually found in C:\Windows\System32\drivers\etc\hosts. By adding these lines to it I can create a domain with two subdomains locally and point them both at my WSL instances IP address:

1
2
172.19.38.94     test-site.mysite.local
172.19.38.94     no-site.mysite.local

After making any changes to the hosts file it is important to flush the DNS cache by running the following in Powershell:

1
ipconfig /flushdns

When visiting those URLs in my browser, and by adding the port, I can see my webapp running on WSL.

In javascript I can read the subdomain with the following:

1
String(window.location.host).split(`.`)[0]

Javascript Railway Board

Using the Transport API I thought it might be fun to build a small web-based board that looked and felt like the electronic display boards seen at British railway stations.

Future plans would be to use a small screen attached to a Raspberry Pi Zero and 3D print a desktop standing sign.

Notes on Hugo 0.60

config.toml

To ensure I can use raw HTML, for example when embedding a YouTube video, I had to set unsafe to true for the goldmark renderer:

1
2
[markup.goldmark.renderer]
  unsafe = true

I wanted my posts URLs to be at the root of the domain, and not at posts/xxxx, this can be achieved in config:

1
2
[permalinks]
  posts = "/:filename/"

I can dynmanically add the current year to the copyright notice by defining my copyright in the config:

1
copyright = "Copyright 2015-{year} - Largely a reference for me, you might find it useful. Sherry?"

Then in my footer.html partial:

1
<small>&copy; {{ replace .Site.Copyright "{year}" now.Year }}</small>

Archetypes

Archetypes pre-fill the frontmatter for a given content type. For example, I have a posts folder in my content folder, and I have a corresponding posts.md in my archetypes folder. The posts.md file looks like this:

1
2
3
4
5
6
7
8
---
title: "{{ replace .Name "-" " " | title }}"
description: ""
date: {{ .Date }}
tags: []
draft: false
---

I have added description and tags to make sure they appear in every new post file, and I’ve set draft to false as I want to explicit set it to true in the rare instance I create a draft and commit it.

Data

As a bit of fun I keep a page with lists of things I like. These are stored as toml under data/xxx.toml, defined in the following way:

1
2
3
4
5
6
7
list = [
  "Blade Runner (1982)",
  "Amelie",
  "Goodbye Lenin (2003)",
  "Bob La Flambeur (1956)",
  "2001 A Space Odyssey"
]

Then I have a shortcode in my <theme>/layouts/shortcodes folder called list.html:

1
2
3
4
5
6
7
8
9
{{ $data := index .Site.Data (.Get 0) }}
<section>
  <h2>{{ .Get 1 }}</h2>
  <ul class='list'>
  {{ range sort $data.list }}
    <li>{{ .  | markdownify }}</li>
  {{ end }}
  </ul>
</section>

The first line above is looking for the first argument passed to the shortcode which will be the name of the data file.

Line 5 grabs and sorts the list, by making sure every list toml file defines the array as list=, this will work for any data file I throw at it:

1
2
3
4
  {{< list "films" "Films" >}}
  {{< list "music" "Music" >}}
  {{< list "books" "Books" >}}
  {{< list "places" "Places I've been" >}}

Pagination

First create a paginator using your where selector and Paginate. Here I am getting all my ‘posts’ and sorting by date, newest first:

1
{{ $paginator := .Paginate (where .Site.RegularPages "Section" "posts").ByDate.Reverse }}

Now in my range I can use the paginator:

1
2
3
{{ range $paginator.Pages }}
...
{{ end }}

The Paginator exposes lots of fun things for us to use. For next and previous links (which I find ambiguous, preferring newer and older), we can test if there is a next or a previous and render some markup accordingly:

1
2
3
4
5
6
{{ if $paginator.HasPrev }}
  <a href="{{ $paginator.Prev.URL }}">&laquo; Newer</a>
{{ end }}
{{ if $paginator.HasNext }}
  <a href="{{ $paginator.Next.URL }}">Older &raquo;</a>
{{ end }}

The number of items per page can be set in the config.toml:

1
paginate = 5

I miss the 1990s