Notes on building a homelab
I have been running a homelab since 2020. It started, as these things do, with a single Raspberry Pi and a misguided ambition to self-host Nextcloud. Six years later it spans three VPS instances across two providers, a private network stitched together with Tailscale, a Docker service stack behind Nginx, and a CI runner that builds my side projects while I sleep.
Most of what I know about computers, I learned by breaking this network and then fixing it.
The shape of the network
The topology is intentionally boring:
┌──────────────────┐
│ Tailscale mesh │
└────────┬─────────┘
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ VPS-1 │◀───────▶│ VPS-2 │◀───────▶│ VPS-3 │
│ (web) │ │ (data) │ │ (CI) │
└─────────┘ └─────────┘ └─────────┘
Each VPS runs a focused role. The web box fronts the public stuff — this site, a couple of side projects. The data box holds the databases and the longer-lived state. The CI box is disposable; if it dies, I rebuild it from a Dockerfile in twenty minutes.
Tailscale is the load-bearing wall. Before I adopted it, every service was a small networking puzzle: firewall rules, reverse tunnels, DNS hacks. After, every box was just on the same network. That single change probably saved me a hundred hours.
Lessons
A short list of things I would tell 2020 me:
- Backups are a feature, not a chore. Treat them like one. Test the restore, not just the backup.
- Boring technology is a competitive advantage — when the cost is your own free time, novelty is expensive.
- Document the why, not the what. The what is in the code. The why evaporates in a week.
- A status page is free accountability. Once you have one, you cannot pretend downtime does not count.
# docker-compose.yml — the part that runs the status page
services:
uptime-kuma:
image: louislam/uptime-kuma:1
restart: unless-stopped
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:3001:3001"
Why bother?
People occasionally ask why I run my own infrastructure when Vercel and Fly.io exist. The honest answer is: because I learn things. Latency, caching, failover, observability — these are abstractions on a managed platform and realities on your own. I do not want to be the engineer who has only ever seen them in a slide deck.
Also, it is fun. That is allowed to be a reason.