Self-hosted K3s Kubernetes cluster running on 3 Ubuntu Linux nodes.
This cluster hosts web applications, databases, data backups, an image registry, and much more.
It's like AWS, if AWS was hosted entirely in my office, next to my cat's food bowl, running on old laptops and my gaming PC from high school.
~ Me, circa Today
Grafana, Harbor, OpenWebUI, Dashboard and Garage reuse each release's
deployed Helm chart version when applying values. Use --dry-run to simulate
their Helm operations before deploying. See version reuse and verification
for first-install behavior, Garage's local chart requirement and test evidence.
Kubernetes' own dashboard for cluster management.
- Deployed using the official guide.
- Headlamp was trialled as an alternative UI, torn down 2026-07-02, and its manifests removed 2026-09-08; the official dashboard is the one that stays.
Persistent storage solution for the entire cluster.
- Deployed using Longhorn's kubectl install guide.
- Backups and redundancy on HDD, databases and caches on SSD/NVMe.
- Successfully prevented me from accidentally erasing an entire hard drive full of family videos.
Mimicking Grafana Cloud's Kubernetes Monitoring without actually using Grafana Cloud.
- Prometheus metrics collection and Grafana frontend via kube-prometheus.
- Alloy for log collection via k8s-monitoring-helm.
- Loki for log aggregation via Loki's official helm chart.
Container image registry with a nice web interface.
- Deployed using Harbor's official helm chart
- Public image repositories for images used in the cluster.
- Read more in the Harbor README.
Web interface for interacting with local LLMs via Ollama.
- Deployed with Helm in the
openwebuinamespace. - Serves as the primary chat interface for the local Ollama instance.
Privacy-respecting metasearch engine, used as the web search backend for OpenWebUI.
- Lightweight deployment with custom settings via ConfigMap.
- Aggregates results from multiple search engines without tracking.
S3-compatible object storage engine.
- Deployed based on Garage's official guide.
- Custom manager container for bucket management with the AWS S3 CLI.
Internal OpenAI-compatible LLM endpoints backed by Ollama and llama.cpp.
- Ollama (timmy, RX 9070 XT):
ollama.llama.svc:11434— primary inference for Claude Code and OpenWebUI; hosts gemma4:12b-it-qat (local) and glm-5.1:cloud (remote) - OV VLM (manu, GTX 1080):
llamacpp-cuda-ovin viking namespace — failover only since the 2026-07-05 cloud cutover (OV's primary VLM is cloud via chat-ollama —gemma4:31b-cloud, non-thinking, settled 2026-07-06); replicas=1 during the soak window, retirement candidate - Embedder (timmy, RX 9070 XT, ROCm):
embedder-qwen.viking.svc:8080— Qwen3-Embedding-4B Q8_0 (2560-dim) for vector embeddings (Deploymentembedder-qwen, migrated back to timmy 2026-07-06 after wemby's failing charging cable/port kept hard-dropping the node; co-resides with Ollama on the 9070 XT, ~5 GB;embedder-qwen-cudaon wemby retained asreplicas=0rollback); legacyembedder-llamacppon wemby deleted 2026-07-04 (768-dim rollback path gone); the mem0 stack that pointed at it was torn down 2026-07-02 (seemem0/TORN-DOWN.md) - Read more in
llama/README.mdandviking/.
Retired 2026-07-16 — the
hermesnamespace (agent, jump terminal, secrets, PVCs, Cloudflare route) was deleted in full. Its mem0 memory backend was itself torn down 2026-07-02 (seemem0/TORN-DOWN.md); OpenViking's knowledge-base tooling has since covered the persistent-memory use case, so the project was retired rather than migrated to a new memory provider. Manifests retained for reference — seehermes/RETIRED.md.
AI agent that used mem0 for persistent memory plus OpenViking knowledge-base tools, an SSH terminal backend, and a Cloudflare-exposed dashboard.
- Read more in
hermes/README.md.
Hierarchical RAG engine with auto-generated L0/L1/L2 semantic indices over a filesystem-shaped knowledge tree.
- S3-backed file storage (Garage), HTTP vector DB, embedder on timmy (RX 9070 XT, ROCm, since 2026-07-06), cloud VLM primary via chat-ollama (manu CUDA as failover since 2026-07-05)
- API + web console at
context.nathanwhyte.dev(console at/studio/, root-key login; the oldviking.nathanwhyte.devov-console was removed) - Endpoint tiers: in-cluster
openviking.viking.svc:1933· LAN192.168.1.19:31933· Tailscale100.95.215.105:31933· publiccontext.nathanwhyte.dev(Cloudflare tunnel) - Read more in
viking/openviking.md.
WireGuard mesh for private admin/network access from off-LAN. All 3 nodes run Tailscale; manu and wemby are HA subnet routers advertising 192.168.1.0/24.
- Read more in
tailscale/README.md.
Knowledge-base API (FastAPI + Postgres/pgvector) with a Slack bot frontend (PROJ-028 stage 1).
- Read more in
omnipendium/README.md.
Self-hosted file server for media and general file sharing.
- Longhorn HDD-backed storage plus an NFS PV for the media library.
Torn down 2026-07-25 — namespace deleted (both Services, the scaled-to-0 Deployment, and the 50 GiB
syncthing-dataPVC). The vault syncs through git; no backup was needed. Manifests retained for reference — seesyncthing/TORN-DOWN.md.
Always-on Syncthing peer for Obsidian/compendium vault sync across MacBook(s), iPad, and phone.
Torn down 2026-07-02 — namespace deleted, final Postgres dump archived locally. Manifests retained for reference — see
mem0/TORN-DOWN.md.
Self-hosted Mem0 memory backend for Hermes (server + Platform→OSS adapter + Postgres/pgvector).
Postgres containers, for apps that need them, with Longhorn SSD storage for replicated persistent storage.
Network-wide ad and tracker blocking, deployed on all three hosts — wemby
(192.168.1.9), manu (192.168.1.10), timmy (192.168.1.19). Each is an
independent instance; clients receive all three, so all three must answer
identically.
Since 2026-08-24 the compose files and the internal DNS record set are
version-controlled here: network/pihole/. Internal
names (registry, k8s, longhorn.nathanwhyte.dev) are served from
FTLCONF_dns_hosts in each host's compose file — not from public DNS, which
no longer carries them.
In-house recursive DNS, deployed alongside each Pi-hole on its own private
dns-network bridge. Pi-hole forwards to it as unbound#53. The three do not
share a recursive resolver, so unbound is not a hidden single point of failure.
Full recursion from the root hints with local DNSSEC validation — no public
upstream resolver is in the path.
See network/pihole/README.md for the operating
procedure and tailscale/README.md for the measured DNS
failover behaviour.
Secure public-facing access to services without exposing my home IP address or opening ports on my router (for most services).
For services that don't use Cloudflare Tunnel, Cloudflare's DNS (plus router port-forwarding) allows for secure access via my domain.