Multi-machine self-hosted home lab — architecture, runbooks, and lessons learned.
Find a file
2026-10-16 10:20:04 -07:00
docs document the recording recovery watchdog and the KTLA evening-to-morning swap 2026-10-16 10:20:04 -07:00
files/sauron harden Sauron idle-suspend: USB-presence gate, resume marker, logind IdleAction=ignore; document page-cache lesson 2026-06-23 07:49:41 -07:00
memory document Jellyfin DVR trickplay sidecar, cleanup service, and channel expansion 2026-10-16 07:00:57 -07:00
project-memory document the recording recovery watchdog and the KTLA evening-to-morning swap 2026-10-16 10:20:04 -07:00
scripts add dvd-image-mac.sh (ddrescue-on-Mac imaging) + recovery session note 2026-09-22 08:36:40 -07:00
.gitignore initial commit — Raymer homelab docs 2026-05-17 11:56:09 -07:00
CONTEXT.md document Jellyfin DVR trickplay sidecar, cleanup service, and channel expansion 2026-10-16 07:00:57 -07:00
README.md session notes: LANtern RCE incident/fix + Services page/disk incident 2026-09-26 13:52:30 -07:00
ROADMAP.md anonymize personal names across docs, document kodi daily sync fix 2026-10-11 08:31:48 -07:00

Raymer Homelab

Authoritative documentation, architecture notes, and operational runbooks for Raymer — a multi-machine self-hosted home lab.

This repository is the source of truth for how the lab is wired together: machines, services, network topology, recovery procedures, and the decisions behind them. The infrastructure itself lives on the physical machines below; this repo captures the map.


Overview

Raymer is a small home lab built around five machines (three bare-metal, one KVM VM, one VPS) plus a custom RPi5 router. It self-hosts the day-to-day services I would otherwise rent — media (Jellyfin), photos (Immich), files (Nextcloud), a media-acquisition pipeline (the *Arr stack), a private git server (Forgejo on top of bare repos), and a remote-access exit node (WireGuard + Tailscale).

The lab prioritises three things, in order:

  1. Reliability — uptime over feature velocity; updates are deliberate, not automatic
  2. Documentation as code — every config is git-tracked and every decision is captured in docs/ or project-memory/
  3. Defense in depth — VLAN segmentation, VPN kill switch on torrent traffic, OAuth2 forward-auth on internet-facing services, encrypted backups

Architecture

Machines

Name Role Hardware Network
sweetpea DNS/DHCP, reverse proxy, git server, VM host Dell OptiPlex 7050 Micro 10.0.50.47
qwerty Docker host — Jellyfin, Immich, Nextcloud, NAS HP EliteDesk 800 G4 Mini 10.0.50.83
arrs-vm *Arr stack, download clients, VPN kill switch KVM VM on sweetpea 192.168.30.40 (VLAN 30)
sauron AI workloads, local LLM inference HP Z640 Workstation 10.0.50.213 (Wake-on-LAN)
oreos WireGuard exit node, Borg backup target Contabo VPS public
RPi5 router LAN routing, firewall, port forwards Raspberry Pi 5 + UE300 USB-Ethernet 10.0.50.1 (OpenWrt)

Network topology

Internet (Spectrum DOCSIS)
   │
   ▼
[ ET2251 modem ] ── [ RPi5 / OpenWrt router ] ── [ TL-SG108E managed switch ]
                                                   │
              ┌────────────────────┬───────────────┼────────────┬──────────────┐
              ▼                    ▼               ▼            ▼              ▼
          sweetpea              qwerty          sauron       Deco AP    MoCA bridge
          (10.0.50.47)        (10.0.50.83)   (10.0.50.213)    (WiFi)    (bedroom coax)
              │
              ├── VLAN 30 (br-vlan30) ──► arrs-vm (192.168.30.40)
              │                              └── Gluetun VPN kill switch
              │                                    └── qBittorrent + NZBGet
              │
              └── DNS (.home zone) / DHCP (dnsmasq) / nginx + OAuth2

LAN-side DNS resolves *.home to local services via split-DNS on sweetpea. The *Arr stack is air-gapped on VLAN 30; all download traffic exits through a Gluetun VPN container with kill-switch enabled.


Services

Media

  • Jellyfin — media server on qwerty, reads directly from a mergerfs + SnapRAID NAS pool
  • Seerr — request UI on top of Jellyfin/Sonarr/Radarr (formerly Jellyseerr, migrated to the maintained fork)
  • Sonarr / Radarr / Lidarr / Readarr — TV, movie, music, and book managers on arrs-vm
  • Prowlarr — unified indexer manager feeding the *Arrs
  • Bazarr — subtitle automation
  • qBittorrent + NZBGet — download clients, both routed through Gluetun
  • Calibre-Web / LazyLibrarian — book library UIs
  • Navidrome — music streaming (Subsonic-compatible), on qwerty
  • Audiobookshelf — audiobooks and podcasts, on qwerty

Files and photos

  • Nextcloud (FPM + Postgres + Redis) — files, calendars, contacts, notes, deck
  • Immich — photo backup and library, with automatic mobile sync
  • Samba — local SMB shares for ad-hoc transfers

Networking and access

  • dnsmasq — DNS + DHCP for the LAN, with the .home zone for local services
  • nginx + OAuth2-Proxy — reverse proxy on sweetpea fronting all public services, GitHub OAuth as the auth provider
  • WireGuard — two tunnels: one for routing all arrs-vm egress through the VPS, one for selective remote access
  • Tailscale — peer-to-peer remote access overlay
  • Forgejo — self-hosted git web UI mirrored from bare repos via post-receive hooks
  • Borg — encrypted backups to the VPS

Compute and AI

  • Ollama on sauron — local LLM inference (Llama 3, Mistral, etc.)
  • FastAPI summariser — Python service wrapping Ollama for content summarisation
  • Watchtower — auto-updates *Arr stack containers nightly (scoped to arrs-vm only)

Monitoring and dashboards

  • LANtern — self-hosted network-observability stack on sweetpea (lantern.home): device inventory, DNS / DHCP / NetFlow ingest into SQLite, an activity oracle for idle-gated maintenance, and an SSH / fail2ban security view. React UI + FastAPI; local-LLM query layer planned. Also public on GitHub.
  • Uptime Kuma — service + host uptime monitoring at status.home (LAN + Tailscale), Pushbullet alerts, plus shareable status pages.
  • homepage — two gethomepage.dev launcher instances: home.home (internal, all services; LAN + Tailscale) and janovitch.com (public, the public *.janovitch.com services). One config per instance.
  • fail2ban — SSH brute-force protection on sweetpea, using the systemd-journal backend + aggressive filter (catches low-and-slow scanners). See docs/sweetpea.md.

Notable patterns

A few design choices that distinguish this lab from a typical home setup:

  • VLAN-segregated VPN kill switch. The *Arr stack runs in its own VLAN with all egress forced through Gluetun. Any VPN drop fails the container's network — no torrent traffic ever leaks to the LAN's WAN.
  • mergerfs + SnapRAID NAS. A union filesystem of independent drives with snapshot-style parity, giving JBOD flexibility with single-disk-loss tolerance. No striping, no RAID rebuild penalty.
  • Custom OpenWrt router on RPi5. Replaced a vendor mesh router with a Raspberry Pi 5 + USB-Ethernet adapter, running OpenWrt for full control of routing, firewall, static routes between VLANs, and DNS overrides.
  • Self-hosted git, bare repos as source of truth. Bare repos on sweetpea (accessed via git@sweetpea-git:) push to Forgejo for web UI via a shared post-receive hook. No vendor lock-in to the web frontend.
  • Split-DNS with local TLS. *.home domains resolve to LAN IPs internally and to the public address externally, with separate certificate handling for each path.
  • Documentation discipline. Every machine has a docs/<host>.md; every non-trivial decision has a project-memory/ writeup; every session has a memory/YYYY-MM-DD.md runbook. The lab is reproducible from this repo plus the bare repos on sweetpea.
  • OAuth2 forward-auth on the reverse proxy. Internet-exposed services sit behind a single OAuth2-Proxy instance using GitHub as the provider, so authentication is centralised rather than per-service.

Tech stack

OpenWrt · Docker · Docker Compose · KVM/libvirt · Ubuntu Server · NixOS (laptop client) · dnsmasq · nginx · OAuth2-Proxy · WireGuard · Tailscale · mergerfs · SnapRAID · Postgres · Redis · Forgejo · Borg · systemd · sops-nix (secrets)


Repository layout

The memory/ files are session-level runbooks: each one captures what happened, what was tried, and what was learned during a particular work session. The project-memory/ files are the distilled, durable record — architecture decisions, multi-day initiatives, incident write-ups.


The Raymer documentation references and depends on several other tracked repos:

Repo Lives on Purpose
sweetpea-nginx-config sweetpea nginx vhosts, snippets, upstreams
sweetpea-reverse-proxy sweetpea nginx + OAuth2-Proxy Docker Compose
dnsmasq-config sweetpea DNS / DHCP configuration
sweetpea-scripts sweetpea fail2ban jail, logrotate configs, deco-reboot script
lantern sweetpea LANtern network-observability stack (also public on GitHub)
uptime-kuma sweetpea uptime-kuma monitor (compose)
homepage sweetpea internal home.home launcher (compose + config)
homepage-public sweetpea public janovitch.com launcher (compose + config)
arr-stack arrs-vm *Arr stack Docker Compose
nixos-config laptop NixOS system config for the client machine

Each tracked repo is the source of truth for its machine's configuration; this docs repo describes how they fit together.


Status

The lab is in continuous use as my primary home server. Major recent work:

  • 2026-05 — replaced the Orbi mesh with a custom RPi5 + OpenWrt router (Phase 1), then a TP-Link Deco AP mesh (Phase 1.5). Phase 2 (managed APs with VLAN-per-SSID) is on the roadmap.
  • 2026-05 — re-numbered the LAN from 192.168.x.x to 10.0.50.0/24 to make room for proper VLAN segmentation.
  • 2026-07 — LANtern went operational (device inventory, DNS/DHCP/NetFlow ingest, an idle-detection activity oracle, and an SSH/fail2ban security view at lantern.home). Added uptime-kuma monitoring (status.home) and homepage launchers (internal home.home + public janovitch.com), and hardened fail2ban (systemd-journal backend + aggressive filter) to catch low-and-slow SSH brute-forcers.
  • 2026-09 — repaired Jellyfin fallout from the media-normalize batch: 18 movie collections had lost members (Twilight fully empty) because BoxSet membership is pinned by file path and the pipeline had changed extensions .mp4→.mkv; re-added the 36 members and rewrote each collection.xml to current paths. Also fixed 168 movies displaying raw release names (junk movie.nfo <title>) via a TMDB metadata refresh. Root cause (normalize pipeline not collection-/NFO-aware) is on the roadmap — it will recur until the pipeline is fixed.
  • 2026-09 — found and shut down a 10+ day live RCE exploitation attempt against Nextcloud (new fail2ban jail + a Cloudflare real-IP fix), and fixed a 2-month blind spot in LANtern's own security monitoring (a logrotate/Docker bind-mount gotcha — see Raymer/memory/2026-09-20.md). Added LANtern's Services page (Jellyfin watch history, its first cross-host data source: a small pusher script on qwerty rather than a colocated ingest container — see Raymer/memory/2026-09-26.md).
  • Ongoing — extending LANtern toward its local-LLM query layer and dynamic SnapRAID sync (currently validating thresholds in shadow mode; the Deco reboot side of idle-gated maintenance has been live since 2026-08-29), plus Phase 2 managed APs with VLAN-per-SSID.

See ROADMAP.md for the active and deferred work list.