Simple sites are amazing

I'm leaving my current job. As part of my off-boarding ceremonies I decided to give people all my contacts so they can find me afterwards.

I could collect all the relevant links and send them in a giant slack message, but that is not great.

Then I remembered about Linktree and all the people online using it to link all their social media. It is a really good product and the idea of a site just for doing link aggregation makes a lot of sense now that our digital life is spread in multiple places. Nobody needs their own website nowadays, but a single page will all the links that other people might want about them is a really useful proposition.

However, for me it makes no sense to pay to use something so simple that I can probably setup something in a few minutes and not mess much more with that for a long time (to be fair, Linktree has a bunch of features, but nothing that makes it worth for me).

Then I started looking on GitHub for Linktree alternatives and found a few ones, some of them were really refreshing on how simple they were, e.g.:

However, I decided I wanted to write my own as a fun exercise. I wanted it to be written in Phoenix so I can also host some LiveView experiments I want to make soon, although for now it will be just a simple page with a few links.

The result you can check out at

K8s dev cluster with Kind and Tailscale

Recently at my work we started using Kind and ctlptl to bring up a local Kubernetes cluster to use in development together with Tilt.

This setup worked perfectly fine. However, when you want to run too many things (like our entire observability stack with Grafana, Prometheus, Loki and Tempo) it can take a hit on your CPU. Even then, albeit the MacBook Pro fan going wild, it was still usable.

That's until I tried to stream a workshop using mmhmm, being transmitted through Zoom. Suddenly, the local Kubernetes cluster, mmhmm and Zoom started competing for CPU and the stream frames-per-second went down by a lot (like maybe less than 1 FPS).

At that point, I had an option:

  • I could do the workshop in my Linux desktop (which has a way more powerful CPU) but replace mmhmm with something like OBS
  • I could stay on Mac, use mmhmm but run the Kubernetes cluster somewhere else (ideally on my Linux desktop).

I didn't liked the first option because mmhmm makes the whole presentation more entertaining. So I started exploring the second option.

The Plan

I wanted to be able to have a development cluster on my Linux desktop running but still be able to access it from outside my home network. This is where Tailscale comes in. Tailscale basically provides a WireGuard based VPN without any complex configuration.

So my plan was:

  • Setup a Tailscale network with both my desktop and my Macbook Pro
  • Setup a Kind cluster in my desktop
  • Expose that cluster to the Tailscale IP for my desktop
  • Setup configuration so Tilt can correctly detect the registry

Making it work

I had to do and redo the plan a few times, but this is how I managed to make it work:

On my desktop I setup Tailscale first and then got my tailscale ip with

tailscale ip -4

Then I created a file describing my shared cluster:

kind: Registry
port: 35000
listenAddress: "<DESKTOP-TAILSCALE-IP>"
name: shared-registry
kind: Cluster
product: kind
registry: shared-registry
  name: shared-cluster
    apiServerAddress: "<DESKTOP-TAILSCALE-IP>"
    apiServerPort: 35443

Then I created the cluster with

ctlptl apply -f shared-cluster.yaml

After this was setup, I thought I was done, but after some tasks I realized I need to do some more things to allow Tilt on the other side to work properly.

The way Tilt automatically finds the registry configuration for a cluster is using KEP-1755. The way it works is that it have a ConfigMap in the kube-public namespace named local-registry-hosting. However, even though I specified listenAddress to ctlptl, it still created it with the wrong configuration (host was set to localhost:35000). So I edited the config map to look like this:

apiVersion: v1
  localRegistryHosting.v1: |
    host: <DESKTOP-TAILSCALE-IP>:35000
    hostFromClusterNetwork: shared-registry:5000
    hostFromContainerRuntime: shared-registry:5000
kind: ConfigMap
  name: local-registry-hosting
  namespace: kube-public

After that, I needed to setup on my MacBook that the registry <DESKTOP-TAILSCALE-IP>:35000 is an insecure registry. It basically means ensuring the Docker config has the insecure-registries config:

  "insecure-registries": ["<DESKTOP-TAILSCALE-IP>:35000"]

You can check the Docker documentation about Insecure Registries for that.

If you don't plan to build docker images and push from the desktop itself, you can instead of setting the insecure registries, you can add hostFromContainerRuntime: localhost:35000 to the localRegistryHosting.v1 config as any localhost registries are already treated as insecure.

Another learning: Tilt is smarter than I thought

For a long time I've been setting the image when deploying with Tilt to be something like localhost:35000/my-image:latest. However, I just figured out Tilt is smarter than that when it can discover the registry for the cluster using KEP-1755 you just need to make sure the image name on the docker_build command is the same on your Kubernetes manifest and it will automatically replace with a image in the local registry it discovered.

Testing Listed and Standard Notes

It's been a while since I've blogged. I tried it all over my career: WordPress, Ghost, Medium, Jekyll (and other static site generators).

Each one of them had their upsides/downsides. Ultimately, I realized I wanted something really simple to be able to write my thoughts without much hassle.

That's where Listed + Standard Notes comes into play. I'm excited to be trying this out to share my thoughts.

This is my test blog post to get this started. Let's see how that goes.