field guide
field guide / how to read the map above

Seven machines, seventy services, and the reasoning in between.

The map above is a homelab — a small cluster of computers in one house, carrying the services a household actually uses: media, photos, documents, chat, DNS, identity. It is built and operated the way a production system would be, at a scale one person can hold in their head. This guide is the written half of that: how to read the wires, what each machine is for, and the decisions that put things where they are.

7machines
70services
8kinds of traffic
95mapped pathways

01 How to read the map hover any box to trace just its wires

Every line is a real pathway between two services, coloured by the kind of traffic it carries. Hover a box to light up only its connections and dim the rest; the switches in the top bar turn each traffic type on and off. Four are shown by default — the ones running constantly.

metricsNumbers, pulled on a timer. One server asks every machine “how are you doing?” every 15 seconds and records the answer.
logsText. Each machine streams its system and container logs into one searchable pile you can grep across.
alertsWhen a number crosses a line or a log says something bad, this is the path a warning takes to reach a phone.
probeReachability checks. A prober pings services and the open internet from the outside to confirm they actually answer.
tlsThe encrypted front door. One reverse proxy puts every internal service behind a real HTTPS certificate. off by default
dnsName lookups. The network runs its own resolver, so every device asks it to turn names into addresses. off by default
authSingle sign-on. One login guards the private dashboards instead of a password per app. off by default
syncThe repo heartbeat. Hourly jobs that pull running config back into Git — the loop explained in section 04. off by default

Showing by default: metrics · logs · alerts · probe. Flip the other four on from the traffic switches in the top bar to see encryption, DNS, login and the Git heartbeat overlaid.

A few boxes have no wires. Nine services are drawn with no pathway attached — not because they are isolated, but because their wiring was not confirmed against a config file when this snapshot was taken. A line on this map means a relationship someone verified in the source; the alternative is a diagram that looks complete and is partly fiction.

02 The seven machines each box in the map is a host

Most of the boxes on the map are a single physical computer; a couple are the network gear — the firewall and the switching fabric — that everything else routes through, drawn here because the lab watches them just as closely as the servers. One machine is the deliberate exception: the Raspberry Pi 5 appears as two boxes. Its monitoring stack and its scheduled control jobs share a single board, but they reboot, fail, and get tinkered with on entirely different schedules — so it is clearer to read them as two separate hosts.

synology · gcloudstorage and everything next to itSynology NAS · big disk

The storage box, and everything that needs to sit next to storage: the media library and its playback server, the photo library, the document archive, the Matrix homeserver, a Git forge, and the smart-home hub. Its processor has no video-acceleration silicon, so transcoding runs in software on the same cores as everything else — which makes CPU contention the design constraint here rather than disk. Two consequences follow. The heavy indexing jobs (photo machine-learning, OCR) are pinned to a subset of cores so they cannot starve playback, and they are deferred wholesale into a weekly overnight window instead of running on arrival. The box is kept deliberately uncrowded for the same reason: every spare cycle is one a transcode can use.

runsplex · arrimmichpaperlessmatrix · minioforgejohome-assistantunifi controller
pi5 · vanahiem · logstackthe collection pointRaspberry Pi 5 · 8 GB

The observability core — the upper of the two Pi 5 boxes. VictoriaMetrics stores every number and vmalert evaluates the rules against it, Loki holds every log, Vector pipes logs into it, and Grafana draws the graphs. Roughly a dozen small exporters translate each device into numbers the store can read. If you want to know the state of anything in the lab, you look here.

This ran on Prometheus until mid-2026. The migration was driven by memory and retention on an 8 GB single-board computer, not by novelty: VictoriaMetrics took over as the authoritative store, ran alongside for verification, and the Prometheus container was removed once its dashboards and rules had been proven against the replacement. Its config layout was kept as-is, because the new store reads it directly and a rewrite would have been churn for its own sake.

runsvictoria-metrics · vmalertlokivectorgrafanablackbox + exporters
pi5 · vanahiem · control nodeidentity, dashboards, scheduled jobssame Pi 5 · systemd timers

The same Pi 5, in its other role. This is the desk that actually gets tinkered at: the identity provider that fronts the private services, the dashboards, and the scheduled jobs that keep the Git repository honest — the hourly sync, the weekly snapshot, the daily dashboard export. Because it is the machine that gets poked and rebooted, the lab is arranged so that nothing critical to noticing problems depends on it staying up.

The dashboard here has been replaced once. A configuration-file dashboard did the job for a year and was retired in favour of hearth, written for this lab specifically, because the remaining work had stopped being configuration and started being interface. The old one was kept running for two weeks after it stopped being used — not out of sentiment, but because its container was still the source of another service's runtime secrets, and decoupling that was a separate change that deserved its own step.

runsauthentik (SSO)hearthgverse-hubly-portalvaultwardenhomelab-sync
pi4 · nysathe resilience anchorRaspberry Pi 4 · big SSD, mostly empty

The machine that is deliberately never touched. It carries the jobs that must keep working while everything else is being worked on: network DNS, the HTTPS front door, the uptime monitor, push notifications, and the routing of alerts. It also holds a small self-heal that can restart a logging service on the router when that service dies, and an automatic blocker for hostile source addresses seen at the edge. It runs all of this precisely because it survives a reboot of the collection point — see the second decision below.

runspi-hole (DNS)caddy (TLS)uptime-kumaalertmanager · karmantfy puship-judge
win10 · valhallax86 capacity poolWindows 10 · x86 · big RAM

The one heavyweight x86 box, always on. It hosts the modded Minecraft server and acts as the spare-capacity pool for anything too demanding for a Raspberry Pi or too CPU-hungry to leave on the NAS — batch jobs get relocated here when they turn out to be competing with media playback. Its monitoring agents report back to the collection point like every other host.

runsminecraft serverglances agentrelocated batch work
pfsensethe edgerouter · firewall · no containers

The front gate between the house and the internet. It runs no apps — it routes and filters traffic — but it streams its firewall logs into the collection point and its configuration is snapshotted into the repo every week, so even the gateway is version-controlled.

roleroutingfirewallsyslog source
unifi fabricthe wiring, watched like a hostswitch + access points

Not servers — the wiring and Wi-Fi every other machine talks through. They appear on the map because the lab pings them constantly: if a switch or access point stops answering, you want to know before anything plugged into it does.

roleL2 switchaccess pointsping targets

03 Three decisions that shape everything

↳ the collection spine

Collect everything in one place.

Monitoring is pull-based: one box — the collection point — reaches out and asks every machine for its numbers and logs, rather than each machine needing to know how to reach the dashboards. Adding a host is then a one-line change on one machine instead of a deployment to the new one. The cost is a single busy dependency, which the next decision exists to contain. This is the heaviest traffic in the lab, and why it is on by default above.

↳ the resilience anchor

Keep the alarm on its own circuit.

The thing that tells you something is broken must not share a fate with the thing you are most likely to break. So alert routing, DNS, TLS and uptime checks live on pi4 — the machine nobody touches — while metrics and logs live on the Pi that gets actively worked on. Reboot the collection point and the alarm keeps watching. The trade is that two Pis must be kept healthy instead of one; the failure it prevents is the one where the monitoring goes down and nothing says so.

↳ honest instrumentation

An alert that can't fire is worse than none.

A rule that says “alarm if the last reading is older than ten minutes” silently stops working the moment readings stop existing — the expression returns nothing, and nothing is not an alarm. Every deadman check here is written to fire on absence explicitly, because the quiet version had been failing open. The same rule applies to this page: instrumentation you have not tested is a claim, not a safeguard.

04 The repo is the source of truth GlassBaby/homelab @ main

The lab is defined as code in one Git repository: containers, config files and scheduled jobs exist first as version-controlled text, which makes the repo — not any single machine — the authoritative record of how the system is meant to behave. To answer “what is this lab?” you read the repository, not the running hosts. The honest version of that claim carries an asterisk: a handful of services predate the convention and were built by hand on the box they run on. They are tracked as work to bring in, not quietly counted as compliant.

The mechanism that keeps that claim true is a closed loop, not a one-way deploy. Configuration is committed and rolled out to the hosts in the usual direction; an hourly job then runs the reverse, reading the running state of each machine and committing it back. When something drifts — a value changed by hand, a container updated in place — the next sync records the difference as a commit. The gap between what the repo declares and what is actually running is therefore measurable rather than assumed — for the paths on the sync allowlist. Anything outside it surfaces as a dirty working tree for review instead of being committed automatically, which is the deliberate trade: an unattended job is trusted to record drift, never to decide what counts as intentional.

Secrets are age-encrypted with SOPS before they are ever written to Git, so the repository can hold the complete system — including the encrypted material — without exposing a credential. That is what makes “the repo is the system” a workable claim rather than an aspirational one: there is no second, secret repository where the real configuration lives.

One honest caveat, because the alternative is the kind of claim this page used to make. The map above is not generated from the repository — it is a hand-curated snapshot, dated at the bottom of this page, and it is accurate as of that date and no later. An earlier version of this guide asserted that the diagram “cannot disagree with the code that produced it.” It could, and it did: it went on showing a metrics engine and a dashboard for weeks after both had been decommissioned. Generating it from the repo is the obvious fix and is a known piece of outstanding work. Until that exists, the date is the disclaimer.

01 · commit

describe in code

Every service is a compose file and a config, kept as plain text in the repo.

02 · sync

pull reality back

An hourly timer captures what's actually running and commits it, so the repo stays truthful.

03 · encrypt

secrets stay sealed

Passwords and keys are SOPS-encrypted before they're stored, plus weekly config snapshots.

04 · publish

snapshot, dated

This topology is transcribed from main by hand and stamped with the date it was read.

GLASS/HOMELAB @ main · 7 hosts · 70 services · 95 pathways
SNAPSHOT 2026-07-30