# Home Lab

> A private home fleet that backs up personal data, runs coding agents, filters DNS, and gives each active machine a real job

- HTML version: https://robbiepalmer.me/projects/homelab
- Status: live
- Started: 2026-08-02
- Updated: 2026-10-31
- Technologies: Ansible, AdGuard Home, Hotspot Shield, Proton VPN, Gluetun, t3-code, Claude Code, Codex, Grok Build, opencode, Ente, Backblaze B2, restic, Doppler, Netdata, K3s, Terraform, Hetzner Cloud, CUPS, Docker, Jellyfin, Prowlarr, Sonarr, Radarr, qBittorrent, Recyclarr, Trakt, Colima
- Built on: Build and runtime, Data
- Platform technologies: Nix, NixOS, Tailscale, DVC
- Platform policies: OCI artifacts, Artifact coordinates and digests are independent of the blob provider, Declare services private-only or govern their public ingress separately, Repository declarations are authoritative; catalog records are derived

Read the shorter [project pitch deck](https://robbiepalmer.me/projects/homelab/deck.md).

# Vision

A small, mostly always-on fleet that handles backups, remote development,
network privacy, and monitoring without relying on me to remember each job.

The home lab exists to serve four goals, in priority order:

1. **Data backup and coordination.** The lab syncs and backs up cloud photo
   libraries, datasets, and project files. Manual drive synchronisation has
   failed repeatedly. The machines now own that work.
2. **Agentic development environments.** Always-available machines host my
   coding harnesses (Claude Code, Codex, OpenCode, Grok Build) behind a
   mobile-friendly GUI, so I can drive real development work from my phone over
   the tailnet even when the home power or broadband is unavailable.
3. **Privacy.** Network-wide ad and tracker blocking keeps my devices from
   phoning home to trackers, applied to every device on the LAN regardless of
   what it runs.
4. **Responsible hardware reuse.** Existing devices get useful jobs when they
   fit the workload and the room. Hardware that remains idle, noisy, or costly
   to maintain leaves the active fleet instead of becoming permanent clutter.

The lab is also where I learn networking, DNS, and orchestration across macOS,
Linux, Android, and iOS. Every experiment still has to solve a real problem.

Core services are reachable from my personal devices over the
[Tailscale](/projects/homelab/adrs/000-tailscale) tailnet, so I can inspect and
drive the lab without exposing it to the public internet.

# Problem statement

The problems the lab solves are the recurring ones that manual effort handles
badly:

**Manual data sync is painful and lossy.** Copying photos and files between
drives by hand is easy to forget and easy to get wrong. It also leaves no
audit trail. The cloud photo library has grown into the single source of truth
for family photos, which makes it a single point of failure.

**Agentic development needs a persistent host.** Coding agents are most useful
when they run somewhere always-on with the right repos, credentials, and tools
already prepared. A laptop that sleeps when closed can't drive a session from
a phone. Mobile "cloud" agent environments exist, but they re-bootstrap
context each time, and they're slower, less reliable, and burn more tokens
than a prepared local environment.

**Home devices make a lot of tracking DNS traffic.** Without network-wide
filtering, apps and embedded trackers on phones, TVs, and streaming boxes
resolve ad and analytics domains on every device on the LAN. Fixing this at
the DNS layer covers devices nothing else can be installed on. This adds a
dependency (the resolver), so it has to be built with redundancy from the
start.

**Old hardware has to earn its place.** The Canon MX3100 printer and old phones
still have jobs that use their distinctive physical capabilities. Reuse is
valuable only when the result is more useful than the space, noise, power, and
maintenance it consumes.

# User stories

* As the owner of thousands of family photos, I want them automatically
  synced from the cloud down to a local 10TB drive, so I have an offline
  backup that doesn't depend on a subscription or the internet.
* As a developer working away from my desk, I want to open the home hub's
  agent GUI on my phone and have working repos, credentials, and a choice of
  agents, so I can keep shipping from the sofa.
* As someone with two Codex subscriptions, I want both managed in one place,
  so I can use each plan's allowance and pick the best-fitting one per task.
* As a developer during a home power or broadband outage, I want an off-site
  t3-code environment already running, so coding capacity is available over a
  modest mobile connection without bootstrapping a laptop locally.
* As a household with a home router, I want ad and tracker blocking applied
  network-wide with automatic failover, so every device is protected even if
  one DNS box reboots.
* As someone who owns a 2011 printer, I want to print from my phone remotely
  over CUPS, so I keep using hardware that still works and avoid e-waste.
* As someone sharing living space with the lab, I want every active machine to
  have a real job and an acceptable noise level, so spare hardware does not
  turn the house into a server room.
* As the lab owner, I want Netdata to tell Slack when something is
  overheating, down, or hung, so I find out about problems before they matter.
* As a web developer, I want agents to test changes on real Android 10, newer
  Android, and iOS 15 hardware, so mobile regressions are caught before
  a pull request merges.
* As the person who operates the DNS servers, I want a battery-backed device
  on mobile data to check the lab from outside its own network, so a total DNS,
  power, or internet outage can still raise an alert.
* As someone deciding what to watch from the sofa, I want my Trakt watchlist
  taps to become Jellyfin library entries automatically, so choosing media
  never involves an admin panel.

# Topology

## System context

Personal and household devices reach the lab over its private network. The lab
pulls photos from Ente, works with code and agent providers, follows a Trakt
watchlist to acquire media, and sends operational alerts to Slack.

```mermaid
flowchart TB
Owner["Lab owner"]
Household["Household"]
Lab["Home lab"]
Cloud["Ente"]
Code["GitHub and<br/>AI providers"]
Alerts["Slack"]
Trakt["Trakt"]

Owner -->|"Manages"| Lab
Household -->|"DNS and printing"| Lab
Cloud -->|"Photos"| Lab
Trakt -->|"Watchlist taps"| Lab
Lab -->|"Viewing history"| Trakt
Lab -->|"Code and agents"| Code
Lab -->|"Alerts"| Alerts

classDef person fill:#7c3aed,color:#fff,stroke:#6d28d9
classDef system fill:#2563eb,color:#fff,stroke:#1d4ed8
classDef external fill:#475569,color:#fff,stroke:#334155
class Owner,Household person
class Lab system
class Cloud,Code,Alerts,Trakt external
```

## Containers

### Interactive services

Tailscale is the private entry point for DNS, development, media, and printing.

```mermaid
flowchart TB
People["Owner and household devices"]

subgraph lab["Home lab"]
Access["Tailscale<br/>Private access network"]
DNS["AdGuard Home<br/>Filtered DNS"]
Dev["t3-code and coding agents<br/>Development workspace"]
Media["Jellyfin<br/>Media server"]
Print["CUPS<br/>Print server"]
end

People --> Access
Access --> DNS
Access --> Dev
Access --> Media
Access --> Print
DNS ~~~ Dev ~~~ Media ~~~ Print

classDef external fill:#475569,color:#fff,stroke:#334155
classDef service fill:#0f766e,color:#fff,stroke:#115e59
class People external
class Access,DNS,Dev,Media,Print service
```

### Data and monitoring services

Ente sync and Jellyfin share local storage. The media automation stack pulls a
Trakt watchlist into that storage, and Jellyfin scrobbles plays back so
recommendations improve. Netdata sends operational alerts to Slack, while the
development workspace talks to GitHub and the agent APIs.

```mermaid
flowchart TB
Ente["Ente cloud"] --> Backup["Ente sync<br/>Photo backup"]
Backup --> Storage[("Local storage")]
Storage --> Media["Jellyfin<br/>Media server"]
Tracker["Trakt"] -->|"Watchlist"| Auto["Media automation<br/>Prowlarr, Sonarr,<br/>Radarr, qBittorrent"]
Auto -->|"Downloads"| Storage
Media -->|"Scrobbles"| Tracker
Monitor["Netdata<br/>Monitoring"] --> Slack["Slack"]
Dev["t3-code<br/>Coding agents"] --> Code["GitHub and<br/>AI providers"]

Backup ~~~ Monitor ~~~ Dev

classDef external fill:#475569,color:#fff,stroke:#334155
classDef service fill:#0f766e,color:#fff,stroke:#115e59
classDef data fill:#4338ca,color:#fff,stroke:#3730a3
class Ente,Slack,Code,Tracker external
class Backup,Media,Monitor,Dev,Auto service
class Storage data
```

## Deployment

### Device context

The Mac mini is the home hub. The Raspberry Pi keeps DNS available during hub
maintenance and owns the printer connection.

```mermaid
flowchart TB
Router["Home router"]
Internet["Internet"]
Slack["Slack"]
Printer["Canon MX3100"]
FireTV["Fire TV Stick"]
Drive[("10TB HDD")]

Mini["Mac mini<br/>Home hub"]
Pi["Raspberry Pi<br/>Fallback host"]

Router -->|"Primary DNS"| Mini
Router -->|"Fallback DNS"| Pi
Pi -->|"Metrics"| Mini
Mini --> Drive
Mini --> FireTV
Mini --> Slack
Mini -->|"VPN egress"| Internet
Pi --> Printer

classDef live fill:#0f766e,color:#fff,stroke:#115e59
classDef data fill:#4338ca,color:#fff,stroke:#3730a3
class Mini,Pi live
class Drive data
```

### Mac mini containers

The Mac mini hosts the lab's main services. Hotspot Shield routes its outbound
traffic through an encrypted tunnel, and a launchd supervisor only starts the
media automation stack while that tunnel is up. Inside the stack, Prowlarr
feeds indexers to Sonarr and Radarr, which grab releases through qBittorrent
and import finished downloads into the library; Recyclarr keeps their quality
profiles aligned with the TRaSH guides. A Trakt watchlist feeds both arrs, and
Jellyfin scrobbles plays back.

```mermaid
flowchart TB
subgraph mini["Mac mini"]
DNS["AdGuard Home<br/>Primary DNS"]
VPN["Hotspot Shield<br/>All outbound traffic"]
Dev["t3-code<br/>Coding agents"]
Backup["Ente sync<br/>Photo backup"]
Monitor["Netdata<br/>Parent node"]
KeepAlive["keep-running agent<br/>Stack supervisor"]
Media["Jellyfin<br/>Media server"]
Storage[("10TB HDD")]

subgraph media["Media automation (Docker Compose)"]
Prowlarr["Prowlarr<br/>Indexer proxy"]
Sonarr["Sonarr<br/>TV"]
Radarr["Radarr<br/>Movies"]
QBit["qBittorrent<br/>Downloads"]
Recyclarr["Recyclarr<br/>TRaSH profiles"]
end
end

Trakt["Trakt<br/>Watchlist and tracking"]

DNS ~~~ VPN ~~~ Dev ~~~ Backup ~~~ Monitor
KeepAlive -.->|"Starts only while<br/>the tunnel is up"| media
Backup --> Storage
Media --> Storage
Media --> FireTV["Fire TV Stick"]

Trakt -->|"Watchlist"| Sonarr
Trakt -->|"Watchlist"| Radarr
Media -->|"Scrobbles"| Trakt
Prowlarr -->|"Indexers"| Sonarr
Prowlarr -->|"Indexers"| Radarr
Recyclarr -->|"Profiles"| Sonarr
Recyclarr -->|"Profiles"| Radarr
Sonarr -->|"Releases"| QBit
Radarr -->|"Releases"| QBit
QBit -->|"Completed downloads"| Storage

DNS -->|"Quad9 DNS"| VPN
Dev -->|"Code and agent APIs"| VPN
Backup -->|"Ente sync"| VPN
Monitor -->|"Slack alerts"| VPN
VPN --> Internet["Internet"]

classDef service fill:#0f766e,color:#fff,stroke:#115e59
classDef supervisor fill:#b45309,color:#fff,stroke:#92400e
classDef data fill:#4338ca,color:#fff,stroke:#3730a3
classDef external fill:#475569,color:#fff,stroke:#334155
class DNS,VPN,Dev,Backup,Media,Monitor,Prowlarr,Sonarr,Radarr,QBit,Recyclarr service
class KeepAlive supervisor
class Storage data
class Trakt external
```

### Raspberry Pi containers

The Pi runs the fallback DNS resolver, sends metrics to the Mac mini, and
connects the old Canon printer to the network.

```mermaid
flowchart LR
subgraph pi["Raspberry Pi"]
DNS["AdGuard Home<br/>Fallback DNS"]
Monitor["Netdata<br/>Child node"]
Print["CUPS<br/>Print server"]
end

DNS -->|"Syncs config"| Primary["Primary DNS<br/>Mac mini"]
Monitor -->|"Sends metrics"| Hub["Netdata parent<br/>Mac mini"]
Print --> Printer["Canon MX3100"]

classDef service fill:#0f766e,color:#fff,stroke:#115e59
class DNS,Monitor,Print service
```

### Development planes and proposed devices

The Vector laptop is the local graphical development plane with 64 GB of RAM
and an RTX 4080. The Hetzner plane provides off-site continuity. A quiet NixOS
companion for persistent Linux services remains proposed while its capacity
and the media-storage boundary are measured. The proposed phone lab will run
browser tests, and the View 20 will check the live hosts over cellular so it
can report a home power or internet failure.

```mermaid
flowchart TB
Mini["Mac mini<br/>Home hub"]
Pi["Raspberry Pi<br/>Fallback host"]
Laptop["Vector laptop<br/>NixOS, GNOME, RTX 4080"]
Companion["Quiet NixOS companion<br/>Persistent services"]
Phones["Phone lab<br/>Android and iOS QA"]
Watchdog["View 20<br/>Cellular watchdog"]
Remote["Hetzner VPS<br/>NixOS and independent K3s"]
Slack["Slack"]
GitHub["GitHub<br/>Branches and PRs"]

Laptop -->|"Local t3-code"| GitHub
Mini -.->|"Proposed service migration"| Companion
Mini -->|"Browser tests"| Phones
Watchdog -->|"Checks"| Mini
Watchdog -->|"Checks"| Pi
Watchdog -->|"Alerts"| Slack
Remote -->|"Cloud t3-code"| GitHub
Mini -->|"Home t3-code"| GitHub

classDef live fill:#0f766e,color:#fff,stroke:#115e59
classDef accepted fill:#dbeafe,color:#1e3a8a,stroke:#2563eb
classDef proposed fill:#fef3c7,color:#78350f,stroke:#d97706,stroke-dasharray:5 5
classDef data fill:#4338ca,color:#fff,stroke:#3730a3
class Mini,Pi live
class Laptop accepted
class Companion,Phones,Watchdog,Remote proposed
```

# Nodes

## Mac mini, the home hub

The Mac mini is the primary always-on home-services machine. It owns the photo
backup pipeline and the storage-coupled media services. The Vector has become
the primary development and build machine.

* **Always-on development fallback.** It runs [Claude Code](/projects/homelab/adrs/002-claude-code),
  [Codex](/projects/homelab/adrs/003-codex) (both subscriptions),
  [OpenCode](/projects/homelab/adrs/003-opencode) and
  [Grok Build](/projects/homelab/adrs/002-grok-build), all orchestrated from
  [t3-code](/projects/homelab/adrs/004-t3-code). It remains available for
  lighter sessions and macOS work, while the 64 GB Vector carries primary
  development and builds.
* **Photo backup.** It connects to [Ente](/projects/homelab/adrs/005-ente-photo-backup),
  the end-to-end encrypted cloud photo provider, and regularly syncs my
  library down to a connected 10TB HDD as an offline backup.
* **DNS privacy.** It runs [AdGuard Home](/projects/homelab/adrs/001-adguard-home)
  as the primary DNS server that my router points at. It blocks \~33% of all
  requests over my home network. AdGuard is also reachable over the tailnet,
  so the phone keeps blocking trackers and ads on cellular or any other
  network. A router-level setup cannot provide that coverage.
* **Always-on VPN egress.** The mini runs
  [Hotspot Shield](https://www.hotspotshield.com) as an always-on client, so
  every outbound connection leaves through its tunnel and the ISP sees one
  encrypted stream instead of individual destinations. This forced one config
  change elsewhere: Hotspot Shield kills TLS connections to known
  encrypted-DNS endpoints, so AdGuard's DNS-over-HTTPS upstream stopped
  resolving under the tunnel. The upstreams are now plain UDP Quad9
  (9.9.9.9), which passes through untouched. LAN devices keep their filtering
  either way; only the mini's own traffic is tunnelled.
* **Monitoring.** It runs [Netdata](/projects/homelab/adrs/006-netdata),
  connected to my Slack workspace via a Slack app, to alarm on anything
  going wrong. The media stack feeds it per-service health gauges through
  its StatsD listener, and the supervisor that keeps that stack alive
  restarts containers Docker marks unhealthy, the hung-process case a
  restart policy can never catch.
* **Media.** It runs [Jellyfin](/projects/homelab/adrs/008-jellyfin) to serve
  TV shows and movies to my Fire TV Stick, in Docker Compose under colima
  with its config declared in `homelab/hosts/mac-mini/jellyfin/` in this
  repo. The library lives on the 10TB HDD and is served over the LAN and the
  tailnet only.
* **Media acquisition.** It runs an automated
  [arr stack](/projects/homelab/adrs/013-media-automation-arr-stack),
  [Prowlarr](/projects/homelab/adrs/014-prowlarr-indexer-management),
  Sonarr, Radarr, [qBittorrent](/projects/homelab/adrs/015-single-containerized-torrent-client),
  and [Recyclarr](/projects/homelab/adrs/016-recyclarr-trash-guides), in
  Docker Compose under colima, supervised by a launchd agent that only starts
  it while the VPN tunnel is up ([ADR 017](/projects/homelab/adrs/017-vpn-gated-stack)).
  Trakt watchlist taps become monitored titles, and finished downloads land
  straight in Jellyfin ([ADR 018](/projects/homelab/adrs/018-trakt-watchlist)).

## Remote development plane, proposed

[ADR 021](/projects/homelab/adrs/021-cloud-remote-development-plane) proposes
a long-lived Hetzner VPS so a home power or broadband outage does not remove
all coding capacity. Terraform provisions it, the repository's NixOS flake
declares the host, and an independent single-node K3s cluster runs t3-code for
one operator. Multi-user capacity belongs on dedicated workspace nodes in a
separately tested cluster, not on this VPS.

It does not extend the home cluster across the internet. Home and cloud share
Kustomize bases where useful, but retain separate control planes, overlays,
volumes, authentication homes, and t3-code state. GitHub branches and pull
requests are the handoff between them. Tailscale is the only application access
path; t3-code and the K3s API remain closed to the public internet.

## Personal digital infrastructure boundary

The [Personal Digital Infrastructure](/projects/personal-digital-infrastructure)
project owns provider choice, cross-device access, account policy, recovery,
portability, and the combined subscription budget. The Home Lab implements the
parts that need local machines or scheduled jobs:

* [ADR 023](/projects/homelab/adrs/023-restic-backblaze-personal-archive) keeps
  an independent encrypted archive in Backblaze B2 and requires recovery
  drills before it counts as a backup.
* [ADR 024](/projects/homelab/adrs/024-proton-vpn-programmable-egress) trials
  Proton VPN and Gluetun as a container-scoped, testable replacement for the
  GUI-controlled Hotspot Shield tunnel on the Mac mini.

Document sync, password management, custom-domain email, and the recovery
packet remain useful when the lab is unavailable, so their decisions live in
the separate project. Inbox Signal continues to own mail processing rather
than mailbox-provider selection.

## Raspberry Pi, the independent fallback

A secondary node that removes single points of failure from the two things
the whole house depends on: DNS and monitoring.

* **Secondary AdGuard Home.** The home router also points at the Pi, so if
  either DNS box is down (e.g. during reboots) the other keeps the network
  functioning.
* **Netdata child.** Exports metrics to the Mac mini as the main hub, and
  alerts my Slack workspace if its temperature climbs too high.
* **CUPS print server.** It connects CUPS to my 2011 Canon MX3100 printer.
  Canon no longer supports the printer, but Gutenprint lets every device on
  the network use it, including my phone over Tailscale.

## Vector development workstation, accepted

[ADR 025](/projects/homelab/adrs/025-graphical-nixos-development-workstation)
turns the MSI Vector into a graphical NixOS workstation and an independent
single-node K3s development plane. Its 64 GB of RAM and RTX 4080 provide the
primary local development and GPU capacity while complementing the lighter
everyday Mac and continuity-focused Hetzner server. GNOME, Brave, the T3 Code
client, and network printing keep the machine useful at its own screen.

## Quiet NixOS service companion, proposed

[PDR 001](/projects/homelab/pdrs/001-separate-macos-work-from-persistent-linux-services)
proposes a small, quiet NixOS machine with at least 32 GB of memory to relieve
the 8 GB Mac mini's repeated memory exhaustion and reboots. The proposal does
not yet select hardware or move workloads. Current measurements will size the
machine and establish a reliable boundary for Jellyfin, media automation, VPN
routing, photo export, and the 10 TB disk.

## Phone device lab, proposed

An Honor View 20, Honor Magic5 Pro, and iPhone 6s will become physical test
targets for the coding agents. Wired connections to the Mac mini provide the
most reliable control. Together they cover an old Android release, a newer
Android release, and iOS 15 WebKit.

The [phone device lab ADR](/projects/homelab/adrs/012-phone-device-lab) also
proposes a second job for the View 20. A cheap data SIM, Tailscale, and a small
Termux watchdog would let it check both DNS servers and the hub from cellular.
That closes the monitoring gap where Netdata cannot send a Slack alert because
the network or DNS it depends on has failed.

The phones will keep their stock operating systems. Neither Honor has a useful
supported custom-ROM path, and Linux on the iPhone 6s is still an experiment.
They will not become DNS servers, general compute nodes, or permanent cameras.

# Plan

The lab is being built incrementally, starting from the highest-value
workloads and layering on the rest:

| Phase | Workload                                                     | Status   |
| ----- | ------------------------------------------------------------ | -------- |
| 1     | Photo backup (Ente → 10TB HDD)                               | Live     |
| 2     | Agentic development hub (t3-code + agents)                   | Live     |
| 3     | DNS privacy (AdGuard Home, primary + failover)               | Live     |
| 4     | CUPS printing on the Pi (avoiding printer e-waste)           | Live     |
| 5     | Jellyfin media server for the Fire TV Stick                  | Live     |
| 6     | Physical Android and iOS device lab                          | Proposed |
| 7     | Cellular watchdog on the View 20                             | Proposed |
| 8     | Graphical NixOS development plane on the 4080 laptop         | Accepted |
| 9     | Automated media acquisition (arr stack behind the VPN gate)  | Live     |
| 10    | Trakt watchlist recommendation loop into Jellyfin            | Live     |
| 11    | Off-site t3-code environment on an independent NixOS/K3s VPS | Proposed |
| 12    | Encrypted offsite non-photo archive                          | Proposed |
| 13    | Programmable VPN egress and container kill switch            | Proposed |
| 14    | Quiet NixOS companion for persistent Linux services          | Proposed |

# What this is not

* **Not a public service.** Everything is behind the tailnet. No ports
  forwarded to the internet, no exposed dashboards.
* **Not a data hoard.** Bulk data has a purpose and an owner: the photo
  backup, local dataset copies, and planned media library. Backup scope may
  grow to documents, games, movies, TV, and music, but each addition has to
  earn its place.
* **Not a reason to turn every device into a server.** The phones are useful
  because they are phones. The Mac mini and Pi already run persistent
  services better.
* **Not a fixed shape.** The lab started as a handful of machines doing
  boring jobs. A proper NAS or a compute scheduler may earn a place later.

# Future direction

These ideas remain outside the current build plan:

* **Expanded bulk backup scope.** Game saves, movies, TV, and music need an
  explicit keep-or-replace policy before they enter the document backup. The
  Ente photo system remains separate.
* **Dashboards on this site.** A public page could show selected Netdata
  graphs and lab status without exposing the private services behind them.

# Risks

## Home power removes the development environment

The Mac mini and broadband router share one power and network failure domain.
When both disappear, the agent subscriptions still exist but their prepared
host, repositories, authentication, and t3-code interface are unreachable.

**Mitigation.** ADR 021 proposes an independent cloud t3-code installation
behind Tailscale. The two installations keep separate mutable state and hand
work off through pushed Git branches. An outage drill must prove the cloud
environment works from mobile data before it is treated as continuity.

## DNS is a single point of failure for the whole house

Running a local resolver adds a dependency that didn't exist before. If both
AdGuard Home instances go down, every device in the house loses DNS and the
internet "stops working."

**Mitigation.** The router uses two independent DNS nodes on separate
hardware. The Pi keeps working during Mac mini reboots and vice versa. The
proposed View 20 watchdog will use mobile data to report a failure of both
resolvers.

## The VPN tunnel dies quietly after a reboot

Hotspot Shield ships as an App Store app, and its tunnel only comes back
after a user session starts. An unattended restart (power cut, forced update)
puts the mini back online with traffic egressing from the home IP until
someone logs in.

**Mitigation.** Netdata watches the mini's public IP and alerts Slack when it
falls outside the VPN exit range. Until that alert exists, treat the tunnel as
down after any unattended reboot.

## Photo backup silently failing

A sync pipeline that fails quietly means the library isn't backed up. Catching
that late is only slightly better than not having a backup at all.

**Mitigation.** Netdata monitors the sync process and alerts Slack on
failure or when the HDD fills up. The backup is treated as a system to be
monitored, not a script to be forgotten.

## Agent blast radius

The Mac mini holds credentials, plugins, and browser state for remote agent
sessions, and it also holds personal photos and the backup drive. A broad
permission profile on any one agent could expose unrelated personal data, and
an agent acting wrongly could delete things as easily as read them.

**Mitigation.** The [Codex ADR](/projects/homelab/adrs/003-codex) already
documents the need for least-privilege filesystem rules and sandboxing on
the connected host. This stays a first-class concern as more agents land on
the hub, with backups covering the "wrong deletion" case.

## Mac mini resource exhaustion

The Mac mini has 8 GB of memory but owns the media stack, photo export,
monitoring, VPN-dependent services, and macOS development. It has rebooted
after repeated full-memory conditions. Moving agent development to the Vector
reduces the pressure but does not prove the remaining services fit.

**Mitigation.** Record memory pressure, swap, container use, and reboot
diagnostics under the remaining workload. PDR 001 proposes a quiet NixOS
companion if configuration changes do not remove the bottleneck. Keep
storage-coupled media services together rather than introducing an unreliable
Wi-Fi filesystem dependency.

## Old phones widen the trust boundary

The View 20 runs an old Android release, and the Mac mini will communicate with
all three phones through persistent USB debugging or inspection channels.
Treating them like fully trusted tailnet peers would give them more access than
their jobs require.

**Mitigation.** Give lab phones tagged Tailscale identities with access only
to preview sites and explicit health-check ports. Keep personal accounts,
photos, and general lab credentials off the View 20. USB debugging stays
authorized only for the Mac mini, and the cellular watchdog holds a separate,
narrowly scoped alert credential.

## Media server scope creep

Jellyfin is the lab's most consumer-facing service and the easiest one to
over-invest in.

**Mitigation.** Keep it as one container behind the tailnet, serving the Fire
TV Stick. Add complexity only to fix a playback or operational problem.

## Architecture Decision Records

- [ADR 000: Tailscale](https://robbiepalmer.me/projects/homelab/adrs/000-tailscale.md) — Accepted, 2026-08-02
- [ADR 001: AdGuard Home for DNS](https://robbiepalmer.me/projects/homelab/adrs/001-adguard-home.md) — Accepted, 2026-08-02
- [ADR 002: Grok Build](https://robbiepalmer.me/projects/homelab/adrs/002-grok-build.md) — Accepted, 2026-08-02
- [ADR 003: OpenCode](https://robbiepalmer.me/projects/homelab/adrs/003-opencode.md) — Accepted, 2026-08-02
- [ADR 004: t3-code as the agent orchestrator](https://robbiepalmer.me/projects/homelab/adrs/004-t3-code.md) — Accepted, 2026-08-02
- [ADR 005: Ente for photo backup](https://robbiepalmer.me/projects/homelab/adrs/005-ente-photo-backup.md) — Accepted, 2026-08-02
- [ADR 006: Netdata for monitoring](https://robbiepalmer.me/projects/homelab/adrs/006-netdata.md) — Accepted, 2026-08-02
- [ADR 007: NixOS GPU worker](https://robbiepalmer.me/projects/homelab/adrs/007-nixos-gpu-worker.md) — Accepted, 2026-08-02
- [ADR 008: Jellyfin media server](https://robbiepalmer.me/projects/homelab/adrs/008-jellyfin.md) — Accepted, 2026-08-15
- [ADR 009: CUPS for the unsupported printer](https://robbiepalmer.me/projects/homelab/adrs/009-cups.md) — Accepted, 2026-08-02
- [ADR 010: Resurrecting the first-gen Amazon Echo](https://robbiepalmer.me/projects/homelab/adrs/010-amazon-echo.md) — Rejected, 2026-08-02
- [ADR 011: Basic Memory and SilverBullet for an extensible agentic knowledge base](https://robbiepalmer.me/projects/homelab/adrs/011-basic-memory-silverbullet-agentic-knowledge-base.md) — Proposed, 2026-08-15
- [ADR 012: Old phones as a physical device lab and cellular watchdog](https://robbiepalmer.me/projects/homelab/adrs/012-phone-device-lab.md) — Proposed, 2026-08-22
- [ADR 013: Automated media acquisition](https://robbiepalmer.me/projects/homelab/adrs/013-media-automation-arr-stack.md) — Accepted, 2026-08-23
- [ADR 014: Prowlarr manages the indexers](https://robbiepalmer.me/projects/homelab/adrs/014-prowlarr-indexer-management.md) — Accepted, 2026-08-23
- [ADR 015: One torrent client, containerized](https://robbiepalmer.me/projects/homelab/adrs/015-single-containerized-torrent-client.md) — Accepted, 2026-08-23
- [ADR 016: Recyclarr keeps quality profiles on the TRaSH Guides](https://robbiepalmer.me/projects/homelab/adrs/016-recyclarr-trash-guides.md) — Accepted, 2026-08-23
- [ADR 017: the media stack waits for the VPN tunnel](https://robbiepalmer.me/projects/homelab/adrs/017-vpn-gated-stack.md) — Accepted, 2026-08-23
- [ADR 018: Trakt drives the watchlist](https://robbiepalmer.me/projects/homelab/adrs/018-trakt-watchlist.md) — Accepted, 2026-08-23
- [ADR 019: Ansible as a bridge to K3s](https://robbiepalmer.me/projects/homelab/adrs/019-ansible-k3s-migration-bridge.md) — Accepted, 2026-09-01
- [ADR 020: K3s for declarative homelab workloads](https://robbiepalmer.me/projects/homelab/adrs/020-k3s-declarative-workloads.md) — Proposed, 2026-09-01
- [ADR 021: Cloud remote-development plane](https://robbiepalmer.me/projects/homelab/adrs/021-cloud-remote-development-plane.md) — Proposed, 2026-09-04
- [ADR 022: Terraform-managed tailnet policy and scheduled drift detection](https://robbiepalmer.me/projects/homelab/adrs/022-terraform-managed-tailnet-policy.md) — Proposed, 2026-10-08
- [ADR 023: Restic and Backblaze B2 for the offsite non-photo archive](https://robbiepalmer.me/projects/homelab/adrs/023-restic-backblaze-personal-archive.md) — Proposed, 2026-10-18
- [ADR 024: Trial Proton VPN for programmable egress](https://robbiepalmer.me/projects/homelab/adrs/024-proton-vpn-programmable-egress.md) — Proposed, 2026-10-18
- [ADR 025: Graphical NixOS development workstation on the Vector laptop](https://robbiepalmer.me/projects/homelab/adrs/025-graphical-nixos-development-workstation.md) — Accepted, 2026-10-24
- [ADR 026: Vault-assisted remote boot for the Vector laptop](https://robbiepalmer.me/projects/homelab/adrs/026-vault-assisted-vector-remote-boot.md) — Accepted, 2026-10-24
- [ADR 027: Retire the legacy NixOS GPU worker](https://robbiepalmer.me/projects/homelab/adrs/027-retire-legacy-nixos-gpu-worker.md) — Accepted, 2026-10-31

## Product decisions

- [PDR 001: Separate macOS work from persistent Linux services](https://robbiepalmer.me/projects/homelab/pdrs/001-separate-macos-work-from-persistent-linux-services.md) · Proposed
- [PDR 000: Keep active home-lab hardware quiet and role-driven](https://robbiepalmer.me/projects/homelab/pdrs/000-keep-active-home-lab-hardware-quiet-and-role-driven.md) · Accepted

---

Markdown index of this site: https://robbiepalmer.me/llms.txt
