Kevin Yoder
Platform & Infrastructure · Flagship

Self-Hosted Home-Server Platform

One Ubuntu machine turned into a 46-container home cloud with an AI-native control plane — a shared model gateway, a pipeline that builds and deploys its own services, and the ops tooling to keep the whole thing alive.

Live · running platform Built Mar–Apr 2026 229 commits Architecture showcase

01 Overview

A single-operator, self-hosted platform that turns one laptop-class Ubuntu box into a home cloud: roughly 95 service definitions, about 46 live containers, all defined in code and kept honest by its own monitoring. On top of it sit the things people actually use — media (Immich, Jellyfin, Seafile), Home Assistant, a dental-AI suite, games, and generative-AI apps. The part I'm showing here is the platform underneath them, which I think of as four layers.

Layer · infra

Infrastructure

The whole server is written down as a recipe — 7 config files, split by category — so it can be rebuilt from scratch. One “front door” sends each web address to the right service behind it; the same friendly names work whether you're at home or dialing in remotely; the firewall blocks everything by default and opens only what's explicitly allowed; and storage is split across three drives with set roles.

Layer · AI

AI gateway

One “brain” that every AI app talks to, fronting ~70 model options. They sit in 20 fallback groups: ask for a model, and if it's busy, rate-limited, or down, the request automatically slides to the next-best one in the group — so an app never just fails. Every call is also recorded end-to-end — a flight recorder for AI requests (what was asked, which model answered, how long it took, what it cost).

Layer · apps

Control plane

First-party dashboards: a self-building deploy pipeline, a live vital-signs board for the machine's hardware (CPU, memory, temperature, disk), a log-scan incident responder, a dashboard editor, a file browser, and a web terminal.

Layer · ops

Reliability

The server keeps an eye on its own health — a 13-point check every 15 minutes — and texts my phone the moment something looks off. It also backs itself up every night (carefully, so a broken backup can't quietly cause harm), tidies up old files on a schedule, and restarts everything in the right order after a reboot.

02 Why I built it

There was never a single manifesto — the platform grew out of a few concrete wants. I wanted to serve everything from one box and reach it from anywhere without opening a single port to the internet, which is what pushed me toward a Tailscale mesh with split-DNS. I wanted building a new service to be self-service and safe rather than an evening of manual scaffolding, which became the deploy pipeline. And a couple of the layers exist because something broke: a runaway backup once filled the root disk unnoticed for days, so alerting had to reach my phone, and a couple of GPUs double as inference nodes when they're not needed elsewhere, so routing had to know when one was actually in personal use and get out of the way.

03 What I built & how it works

No open ports except a Tailscale funnel; everything behind a reverse proxy on a shared Docker network.

Internet no open ports · Tailscale only Tailscale Funnel home server · Ubuntu 24.04 · single mesh ingress dnsmasq split-DNS → Caddy proxy LAN + Tailnet resolve the same hostnames docker network · ~46 containers AI gateway · LiteLLM :4000 Postgres · Phoenix traces · JuiceGpuGate + param-fixer + RAG-inject ~70 aliases · 20 fallback groups · Juice GPU-over-IP → laptop / PC pipeline-dash · self-building deploy describe → … → deploy · 8 human-gated stages control plane · first-party dashboards homeserver-dash · system-911 · homepage-manager file-manager · web-terminal · gethomepage cron · monitor (*/15 → ntfy) · backup (2 AM) · maintenance (3 AM)

Fig. 1 — traffic enters over Tailscale, resolves through split-DNS and Caddy, and lands on a shared Docker network; cron jobs keep the fleet healthy underneath.

The most involved piece is the deploy pipeline (pipeline-dash): it takes a one-sentence description of a service to build and walks it through eight stages, pausing for me to approve before anything runs and again to review a diff before anything ships.

  1. Synthesize — repomix packs each directory into a per-repo context bundle (ctx-synthesize) for the agents to work from.
  2. Research — free-tier web research (Gemini google-search + SearXNG + a reader).
  3. Plan — a structured implementation plan; home-server-specific keywords route the request to fine-tuned models.
  4. Validate — an LLM-as-judge scores the plan against five criteria before it proceeds.
  5. Approve — a human gate; nothing executes until I say so.
  6. Execute — one of 16 coding-agent executors builds it; a bare-git snapshot taken beforehand is kept as the baseline the review step diffs and reverts against.
  7. Review — a unified diff with a destructive-change banner on critical files; reject runs git clean and reverts everything.
  8. Deploy — a genuinely new service gets a data dir, a firewall rule, a build, and a 90-second health-wait.

04 🛠 Skills & tech used

Languages
Bash (advanced)Node 20Python 3.12YAML at scaleSQL / SQLite
ML / AI
LiteLLM proxy + custom callbacksLLM-as-judge validationmulti-agent executor orchestrationstatic-context RAG injectionArize Phoenix / OpenTelemetryGPU-over-IP (Juice)
Infra / Ops
Docker Compose (include-split)Caddy reverse proxyTailscale Funnel / servednsmasq split-DNSUFW default-denyLVM + loop mountssystemd units + cron
Reliability
13-check health monitorntfy push alerting (throttled)guarded backups + DRdynamic SQLite discoverytiered maintenance / pruning
Frontend
vanilla-JS SPAs (no frameworks / CDNs)xterm.js terminalBootstrap 5 + Chart.js dashboarddrag-and-drop YAML editorSocket.IO + SSE streaming
Data
SQLite schema + migrations (×3 apps)PostgresMariaDBJSONL / CSV pipelines
Techniques
git-as-safety-net (snapshot / diff / revert)single-flight + debounce schedulingfail-open gatescontainer introspection (procfs / PAM / sockets)documentation-as-code

05 Notable challenges & decisions

Most of the interesting engineering was defensive — keeping automation from quietly breaking the machine it runs on.

GPU etiquette

Routing that gets out of the way when a GPU's needed elsewhere

Two of the inference GPUs are borrowed from a laptop and a desktop PC that get used for their day jobs. A JuiceGpuGate callback checks a host-side server before routing to a remote node; if that GPU is already busy with something else (util ≥ 60% and the top process isn't Ollama) it raises a rate-limit error so the gateway falls through to the next model. The check lives on the host, not in the LiteLLM container, because the container has no SSH — and every gate is fail-open, so a check-server hiccup never blocks a request.

Reliability

A backup that quietly filled the root disk

For about ten days in July, a nightly backup wrote to /mnt/usb-drive-1 while the drive was unmounted, so the writes landed on the root filesystem and pinned it at 100% — unnoticed, because alerts only went to a local log on the same disk. Diagnosis came from a dfdu divergence pointing at a shadowed mountpoint. The fix added mountpoint-and-writability guards (that log to stderr, not the possibly-missing mount) and moved alerting to ntfy phone push so a silent failure can't happen the same way twice.

Guardrails

Letting an AI deploy without letting it destroy

The execute stage runs coding agents with real reach, so it's wrapped in a bare-git snapshot of the repo taken before anything runs. The review step renders a unified diff, annotates >50% line loss on compose / Caddy / LiteLLM / .env files with a destructive-change banner, and on reject runs git checkout . && git clean -fd to put everything back. A separate registry records deliberately-uninstalled services so automation doesn't resurrect them.

Incident response

An incident responder that had to survive incidents

system-911 scans every container's logs, fingerprints issues by md5 so duplicates collapse (about 184k rows accumulated), attaches likely-cause hints, and can launch an AI fix agent in an embedded terminal with a live per-session cost counter. Docker-event bursts once stacked concurrent scans and OOM-killed it (exit 134); the fix was a single-flight + trailing-debounce scheduler plus a raised memory ceiling.

The trust model is deliberate — and it's a project of its own. The dashboards run on a LAN-trust assumption and the pipeline holds broad privilege by design (it deploys services and opens ports). That's fine for one operator on a private tailnet; the full security posture — the host/network/container hardening, a multi-service audit, and an in-progress pass that tightens exactly these soft spots — lives on the Network Security & Administration page.

06 Results

~46
live containers on the fleet
~95
service definitions in code
~70
model aliases across providers (20 fallback groups)
7
layered compose files
229
commits (Mar–Apr 2026)
8
pipeline stages, human-gated
1,133
pipeline runs recorded (456 completed)
13
health checks each */15-min run

Sources: live docker ps, the repo's git history and compose files, LiteLLM config, and the pipeline / monitor databases on the home server (research snapshot, Jul 2026). Some counts are approximate and drift as the fleet changes.

07 Screenshots

No real captures here yet — the dashboards show live keys, host paths, and log data, so they need a secret- and PHI-scrub pass before publishing. Placeholders below describe the intended shots.

[ pipeline-dash ]
waterfall of step cards → streaming logs → the diff review gate
The deploy pipeline mid-run: step cards lighting up, live executor output streaming, then the diff review gate with a destructive-change banner.
[ homepage ]
~50 live-health tiles across 6 sections
The dashboard hub: health dots across every service, disk / GPU widgets, and a cyberpunk boot-ticker easter egg.
[ system-911 ]
issue cards + xterm.js AI-fix drawer
Severity-grouped issues with likely-cause hints, and the embedded terminal running a fix agent with a live cost counter.
[ homeserver-dash ]
telemetry + Tailscale + geo-flagged logins
Host telemetry: hardware, storage, Docker, the Tailscale peer panel, and a GeoIP-flagged failed-login table.

08 Honest status

This is a live platform I run and depend on daily, which is exactly why it's an architecture showcase rather than something I can hand over as a clone: roughly 95 service definitions, real API keys and host credentials, and my own network wired through the middle. Unlike the smaller projects on this site, there's no de-identified copy to click — the honest version is the writeup and the placeholders above. A couple of caveats I'd rather state plainly: git history stops in early April 2026 even though the platform kept evolving through July (a monitor rewrite, the OOM fix, config touch-ups), so those later changes live as edited files and backups, not commits; and a few older telemetry stacks (Grafana / Prometheus) were retired but still leave stale tiles behind. It's a working system with rough edges, documented rather than hidden.