pieterpel
← All writing
Nixberry · 1 of 5

The whole picture: a Pi, a Hetzner box, and a tailnet

A retro machine shouldn't be a pile of hand-edited configs you're afraid to touch. Here's the system I built so mine rebuilds itself.

Published 2026-07-08 4 min read #nixos #homelab #tailscale

Throughout the past few years I have set up dozens of emulators and subsequently lost countless saves. When I decided to finally wire up a Raspberry Pi 400 that was lying around, I knew that this time I wanted it to be different. This blog series will guide you through the setup I settled on.

The core thesis behind this entire setup is that the emulator running platform should be focused only on that, not on holding hard-fought configuration in random files or storing saves that took hundreds of hours of your precious time. Hence, we need a way to completely decouple configuration from our machines. This is exactly what NixOS was designed for.

Moreover, NixOS gives us a clear and modular framework to consistently create inter-machine processes such as ROM and save syncing. Since all configuration is declarative and stored in Git repositories, we avoid the risk of losing our configuration over time.

One of the hardest challenges of effectively having your machines cooperate with each other is networking and security. Luckily, running Tailscale avoids exposing our machines to the public internet and massively simplifies setting up such a network.

Three machines, one tailnet

The Raspberry Pi 400 is the console. I got it for free a few years ago and it sat collecting dust until this project gave it a job: run games, and hold nothing worth keeping. The Hetzner VM is the half that stays awake — the ROM library, the saves, the metrics. It is a Hetzner box because that was the cheapest VM per month I could find, which is honestly the entire selection criterion; I wanted something that is up whether or not I feel like playing this month. The Mac is where I actually work, so it is the control plane: nix-darwin, on the same tailnet, pushing rebuilds to the Pi and deploys to the VM.

That leaves the saves, and the saves are what the other two are arranged around. Central storage is the only answer that survives changing devices: I have played Pokémon FireRed something like five times on five different machines, and I have zero save files from any of them.

live · homelab.topology hover a node

Everyone in the homelab world knows rule zero: never expose your systems to the public internet. I do not intend to be the exception, and now that automated scanning and LLM-assisted probing are cheap, “unexposed” stopped being a preference and became a requirement.

So the machines do not talk over the internet — they talk over a tailnet. All three join the same WireGuard mesh under MagicDNS names, and from there every connection between them is an ordinary local one: the Pi reaches RomM on the VM by name, RetroArch’s save sync points at a WebDAV endpoint that only exists inside the tailnet, and nothing is listening on a public interface. No port forwards, no certificates to renew, no login page for anyone to find.

Open the deep-dive — the shared tailscale module ›

Every machine on the tailnet needs the same handful of things: the service on, the tailnet interface trusted, and nothing else open. That is small enough to copy by hand — right up to the moment you change a flag and have to remember which machines still carry the old version. So it lives in exactly one place, and every machine gets the change on its next rebuild. One file covers both the Linux boxes and this Mac.

The interesting half is the server branch, which is written for a machine that is administered purely over the tailnet: Tailscale SSH so tailnet nodes get in per ACL without me managing keys, subnet routing in client mode, and a firewall that trusts tailscale0. That machine is the VM.

Sitting down to play

The Pi is never switched off, so there is nothing to wait for. I pick up the TV remote, switch the TV to that input, and Kodi is already there. RetroArch is one entry on the screen. Selecting it gets me into the emulator a few seconds later, having touched nothing but the remote, and the library inside is already organised the way I want it: a playlist per console, built from the same data that lives on the VM. No file browser, and no remembering which folder the SNES games went into.

That is the experience the rest of this series is arranged around, and everything else is what happens behind it — which is also why none of it has to be precious. The console can be wiped and rebuilt at any time without losing anything that matters.