# Frank, the Talos Cluster: Overview & Roadmap

Source: https://blog.derio.net/frank/docs/building/00-overview/
Published: 2026-03-06
Updated: 2026-08-01

The **Frank, the Talos Cluster** series is a walkthrough of building an AI-hybrid Kubernetes homelab from scratch, one layer per post. This post is not part of that walkthrough. It is the index to it: the roadmap, the capability map, the hardware, and a set of commands for checking whether any of it is still true.



## Roadmap



1 Hardware — 7 Nodes, 3 Zones



3x Intel NUC (Core zone) 1x GPU tower — RTX 5070 1x Legacy desktop 2x Raspberry Pi 4



x86_64 arm64 heterogeneous



2 OS & Bootstrap



Talos Linux (immutable) Sidero Omni (lifecycle) Declarative machine config Rolling upgrades



no SSH API-driven reproducible



3 Networking — Cilium CNI



eBPF kube-proxy replacement L2 LoadBalancer (ARP) Hubble observability Network policy



eBPF 192.168.55.200-254



4 Storage — Longhorn



Distributed 3-replica block storage GPU-local StorageClass 2x 4TB SSD on gpu-1 iSCSI via Talos extensions



strict-local best-effort all 7 nodes



5 GPU Compute



NVIDIA GPU Operator (RTX 5070) Intel DRA driver (Arc iGPU) Dynamic Resource Allocation CDI device injection



K8s 1.35 DRA ResourceClaim DeviceClass



6 GitOps — ArgoCD



App-of-Apps pattern Multi-source Applications Self-healing + drift detection Zero-downtime adoption



single repo annotation tracking



7 Fun Stuff



OpenRGB via USB HID DaemonSet + ConfigMap Custom container build (GitHub Actions) IT5701 firmware lock (in progress)



completely unnecessary fans still rainbow



8 Observability



VictoriaMetrics (metrics + alerts) VictoriaLogs (log aggregation) Grafana dashboards Fluent Bit log shipping Blackbox Exporter (endpoint probes) Pushgateway (heartbeat ingestion) Telegram alerting Health Bridge (GitHub lifecycle) Edge Observability (blog analytics, edge security, AI alerts) metrics-server (metrics.k8s.io — kubectl top + CPU/mem HPA)



VMSingle Feature Health metrics.k8s.io health-bridge GoatCounter CrowdSec Falco ai-alert-helper 192.168.55.203



9 Backup



Longhorn → Cloudflare R2 Daily + weekly recurring jobs SOPS-encrypted credentials NAS target (pending Longhorn 1.13)



S3-compatible 7-day RPO



10 Secrets Management



Infisical (self-hosted vault) External Secrets Operator ClusterSecretStore ExternalSecret → K8s Secret



audit trail Universal Auth 192.168.55.204



11 Local Inference



Ollama (gpu-1, RTX 5070) LiteLLM (unified gateway) OpenRouter (free cloud models) OpenAI-compatible API



ollama litellm 192.168.55.206



12 Agentic Control Plane



Sympozium (K8s-native agents) n8n (per-user workflow automation) VK Remote (self-hosted kanban API) ElectricSQL real-time sync



agent=Pod n8n vibekanban 192.168.55.207 192.168.55.216



13 Unified Auth



Authentik IdP (OIDC + proxy) SSO for ArgoCD, Grafana, Infisical Forward auth for Longhorn, Hubble, Sympozium OIDC-backed kubectl via apiserver



OIDC forward-auth 192.168.55.211



14 Multi-tenancy



vCluster (K8s-in-K8s) Disposable experiment clusters Resource quotas + network policies GitOps-provisioned via ArgoCD



vcluster multi-tenant SQLite



15 AI Agent Orchestrator



Paperclip (org-chart agents) Virtual companies + budgets Delegation chains + governance LiteLLM gateway integration



paperclip company model 192.168.55.212



16 Media Generation



ComfyUI (diffusion models) LTX-2.3 video, SDXL image, Stable Audio GPU Switcher dashboard (Go) Time-sharing via replica scaling



comfyui gpu-switcher 192.168.55.213



17 Public Edge — Hop



Hetzner CX23 (single-node Talos) Headscale mesh + Tailscale Caddy reverse proxy + TLS Split-DNS (MagicDNS)



edge WireGuard blog.derio.net



18 Persistent Agent



Kali Linux workstation Always-on Claude Code agent SSH remote access 50Gi persistent /root



kali claude --remote 192.168.55.215



19 Progressive Delivery



Argo Rollouts controller LiteLLM canary (Cilium traffic split) Sympozium blue-green VictoriaMetrics analysis gates



canary blue-green workloadRef



21 Secure Agent Pod



Hardened non-root Kali container Cilium egress allowlist VibeKanban agent orchestration VK Relay (WebSocket tunnel to browser)



security vibekanban relay 192.168.55.215



24 In-Cluster Ingress



Traefik v3 on raspi edge nodes Wildcard TLS (*.cluster.derio.net) Authentik forward-auth (12 services) Homepage dashboard



traefik acme 192.168.55.220



25 CI/CD Platform



Gitea (GitHub mirror forge) Tekton Pipelines + Triggers Zot OCI registry (cosign signed) Webhook-driven CI on pc-1



gitea tekton zot 192.168.55.209



26 Agent Images and the VK-Local Sidecar



agent-images repo (shared base + children) Matrix CI with cross-repo ` repository_dispatch ` VK-local sidecar (shared /home/claude PVC) Lockstep bumper PR in frank



docker github-actions sidecar



29 Ruflo — Swarm Orchestrator



claude-flow / ruvocal (chaotic swarm) Hybrid pod: ruvocal + shell sidecar SSH+Mosh shell on 192.168.55.222 Zero frontier-LLM keys (LiteLLM-only egress)



ruflo claude-flow swarm ruflo.cluster.derio.net



30 The Frank Papers — Research Series



Third blog series (Building / Operating / Papers) Dossier gate (vendors, sources, gaps, counter-args) Mermaid Frank theme + ` .paper-post ` CSS scope Render-time cross-series backlinks (zero retrofit) Paper 00 prologue published 2026-05-18



hugo hextra mermaid research



32 Infrastructure Automation



AWX Ansible controller (operator + CR) Reaches non-Talos / external home-lab devices Native OIDC login via Authentik awx.cluster.derio.net (Traefik)



awx ansible automation



33 Hermes Agent Shell



Nous Research hermes CLI (dedicated pod, gpu-1) BYOK → LiteLLM (provider pinned in config.yaml) profile.d shim vs. sshd env-scrub SSH+Mosh shell on 192.168.55.226



hermes byok litellm



— Virtual Machines — upcoming



KubeVirt (VMs as pods) CDI disk image import KubeVirt Manager UI Longhorn-backed DataVolumes



KVM 192.168.55.205



## Technology → Capability Map



| Technology | Capabilities Unlocked |
| --- | --- |
| **Talos Linux + Omni** | Immutable OS, declarative machine config, secure bootstrap |
| **Cilium (eBPF)** | Kube-proxy replacement, L2 LoadBalancer, Hubble UI (` 192.168.55.202 `) |
| **Longhorn** | Distributed block storage, GPU-local StorageClass, 3-replica HA (High Availability) (**High Availability**Running enough redundant instances that losing one does not take the service down. Frank's three control-plane nodes are its HA tier.) , UI (` 192.168.55.201 `) |
| **ArgoCD** | GitOps, App-of-Apps, self-healing, drift detection |
| **NVIDIA GPU Operator** | GPU scheduling, AI/ML workloads, container toolkit |
| **Intel GPU DRA (Dynamic Resource Allocation) (**Dynamic Resource Allocation**The Kubernetes API for requesting specialised hardware — GPUs and accelerators — with far more expressiveness than the older device-plugin counter model.)  Driver** | iGPU sharing via DRA, namespace-scoped GPU access |
| **OpenRGB** | LED control from K8s (just for fun) |
| **VictoriaMetrics + Grafana** | Cluster-wide metrics, alerting, dashboards, Grafana UI (` 192.168.55.203 `) |
| **VictoriaLogs + Fluent Bit** | Centralised log aggregation and querying |
| **Longhorn Backup + Cloudflare R2** | PVC (PersistentVolumeClaim) (**PersistentVolumeClaim**A Kubernetes request for durable storage. The pod names a claim and the storage layer — Longhorn on Frank — binds real disk behind it, so the data outlives the pod.)  backup/restore, daily + weekly schedules, offsite storage |
| **Infisical + External Secrets Operator** | Secret management with audit trail, ExternalSecret → K8s Secret sync (` 192.168.55.204 `) |
| **Ollama** | Local LLM (Large Language Model) (**Large Language Model**A model trained to predict text, served behind a chat or completion endpoint. On Frank these run locally on the GPU node rather than against a hosted provider.)  inference on gpu-1’s RTX 5070 Ti (16GB). Six base models as of 2026-07-29: Mistral Small 3.2 24B (default), Gemma 4 12B and Qwen2.5-VL 7B (multimodal), Qwen2.5-Coder 14B, Qwen3 14B (reasoning), Qwen3.6 35B-A3B (MoE, partial CPU offload). Lineup lives in ` apps/litellm/values.yaml ` |
| **LiteLLM** | Unified OpenAI-compatible gateway, virtual keys, spend tracking (` 192.168.55.206 `). Local-only since 2026-06-04 — no free-tier cloud aliases |
| **Sympozium** | Kubernetes-native agentic control plane — agent=Pod, policy=CRD (CustomResourceDefinition) (**CustomResourceDefinition**The object that teaches the Kubernetes API a new resource type. Install a CRD and the API server starts serving a kind it has never heard of, with validation and RBAC like any built-in.) , execution=Job (` 192.168.55.207 `) |
| **cert-manager** | Automated TLS (Transport Layer Security) (**Transport Layer Security**The encryption under HTTPS and most other secure protocols. What a certificate is for, and what fails visibly when the certificate does not match the hostname.)  certificate lifecycle for webhooks and internal services |
| **Authentik** | Unified SSO (Single Sign-On) (**Single Sign-On**One login across many applications. On Frank, Authentik holds the session and Traefik asks it before forwarding a request.)  — OIDC (OpenID Connect) (**OpenID Connect**An identity layer on top of OAuth 2.0: it adds a token saying *who* the user is, not merely what the client may do. Frank's single sign-on speaks it, with Authentik as the provider.)  for ArgoCD, Grafana, Infisical; forward-auth proxy for 18 routes incl. Longhorn, Hubble, Tekton Dashboard (` 192.168.55.211 `) |
| **vCluster** | Virtual K8s clusters inside Frank — disposable sandboxes with own API server, resource quotas, network policies |
| **Paperclip** | AI agent orchestrator — virtual companies with org charts, budgets, and delegation chains; complements Sympozium (` 192.168.55.212 `) |
| **ComfyUI** | Diffusion model serving — video (LTX-2.3), image (SDXL), audio (Stable Audio), node-based workflow editor (` 192.168.55.213 `) |
| **GPU Switcher** | Custom Go dashboard for GPU time-sharing — one-click switching between Ollama and ComfyUI (` 192.168.55.214 `) |
| **Hop (Hetzner Edge)** | Public-facing single-node Talos cluster — Headscale mesh, Caddy reverse proxy, blog hosting, split-DNS |
| **Headscale + Tailscale** | WireGuard mesh networking — remote homelab access from any device, MagicDNS for split-DNS |
| **Caddy** | Automatic TLS (Cloudflare DNS challenge), public/mesh routing, path rewriting |
| **Secure Agent Pod** | Hardened non-root coding agent workstation — Cilium egress, dropped capabilities, VibeKanban orchestration, SSH (` 192.168.55.215 `) + UI (` 192.168.55.218 `) |
| **Argo Rollouts** | Progressive delivery — canary (Cilium traffic splitting + VictoriaMetrics analysis) and blue-green (preview + atomic cutover) |
| **n8n** | Per-user workflow automation — 400+ integrations, visual node editor, webhook triggers, Authentik forward-auth (` 192.168.55.216 `) |
| **Blackbox Exporter + Pushgateway** | Feature-level health monitoring — HTTP endpoint probes, cron heartbeat ingestion, Grafana alerting to Telegram |
| **Health Bridge** | Grafana alert → GitHub Project lifecycle state bridge — automatic degraded/dead/healthy transitions, issue comments, bug issue creation |
| **Traefik (in-cluster)** | In-cluster ingress controller, wildcard TLS (` *.cluster.derio.net `), ACME (Automatic Certificate Management Environment) (**Automatic Certificate Management Environment**The protocol Let's Encrypt uses to issue certificates to a server that can prove it controls a domain. Frank's edge proves it over DNS rather than HTTP, which is what lets it get certificates for hosts the internet cannot reach.)  via Cloudflare DNS-01, Authentik forward-auth (` 192.168.55.220 `) |
| **VK (VibeKanban) (**VibeKanban**The task board Frank dispatches agent work through — the queue between a written plan's phases and the agents that execute them.)  Remote (self-hosted)** | Self-hosted VibeKanban kanban API — PostgreSQL 16, ElectricSQL real-time sync, Rust/Axum server (` vk.cluster.derio.net `). Local JWT (JSON Web Token) (**JSON Web Token**A signed, self-describing token carrying claims — who you are, what you may do, when it expires. Readable by anyone holding it, so the expiry and signature are the only things protecting it.)  auth behind an IP allowlist, no forward-auth |
| **VK Relay** | WebSocket relay sidecar tunneling browser API calls to local VK agent server via yamux multiplexing, SPAKE2 (Simple Password-Authenticated Key Exchange) (**Simple Password-Authenticated Key Exchange**A protocol that turns a short shared code into a strong mutual key without exposing the code to eavesdroppers. The enrolment codes are single-use — "please sign in again" usually means it was already spent.)  pairing, Ed25519 request signing |
| **gethomepage.dev** | Cluster dashboard at ` master.cluster.derio.net ` — service catalog with HTTP health indicators, custom bookmarks |
| **Gitea** | Self-hosted git forge with GitHub pull-mirror, Authentik OIDC SSO (` 192.168.55.209 `) |
| **Tekton** | K8s-native CI/CD pipelines — webhook-driven clone, test, build, sign, report status on pc-1 |
| **Zot** | OCI (Open Container Initiative) (**Open Container Initiative**The body behind the standard image and runtime formats. "OCI registry" means any registry speaking that standard, not a specific vendor's.)  container/artifact registry with cert-manager TLS and cosign image signing (` 192.168.55.210 `) |
| **agent-images** | Shared base image + per-pod children repo — ` agent-base ` toolchain + ` secure-agent-kali ` / ` vk-local ` children, matrix CI, cross-repo ` repository_dispatch `, lockstep bumper PR |
| **Ruflo (claude-flow + ruvocal)** | Swarm-style AI orchestrator — hybrid pod (ruvocal SSR (Server-Side Rendering) (**Server-Side Rendering**Building the HTML on the server rather than in the browser. It means the page load can reach databases and APIs, so a "simple" health check on `/` may exercise the entire stack.)  + agent-shell-base sidecar), LiteLLM-only egress, SSH+Mosh shell on ` 192.168.55.222 `, web UI at ` ruflo.cluster.derio.net ` |
| **The Frank Papers** | Third blog series — research-grade landscape reviews framed as decisions; dossier gate (` validate-dossier.py ` + pre-commit hook), Mermaid Frank theme, five ` papers/ ` shortcodes, render-time cross-series backlinks. **Prologue published 2026-05-18:** [Why Run Your Own Cluster in 2026?](https://blog.derio.net/frank/docs/papers/00-why-homelab-in-2026/) |
| **GoatCounter** | Cookieless blog analytics — public beacon via Hop’s Caddy at ` counter.derio.net `, mesh-only admin at ` counter.cluster.derio.net ` with Authentik forward-auth (` 192.168.55.224 `) |
| **CrowdSec + caddy-crowdsec-bouncer** | Edge HTTP security — agent tails Caddy logs on Hop, Caddy bouncer enforces decisions locally without round-tripping to Frank |
| **Falco (modern_ebpf) + Falcosidekick** | Container runtime security on Talos — Loki output to VictoriaLogs (Loki push protocol) + direct Telegram for ` priority:critical ` |
| **alert-agent** | Autonomous ` claude ` agent on multi-agent-shell — daily blog digest, Grafana-alert triage, traffic-surge detection, inbound Telegram Q&A. HTTP-only, no kube credential; deterministic facts from a ` frank-facts ` CLI. Replaced the FastAPI ` ai-alert-helper ` on 2026-06-16 |
| **AWX** | Ansible automation controller — the imperative arm reaching non-Talos home-lab hosts over SSH; operator + ` AWX ` CR (Custom Resource) (**Custom Resource**An object of a type Kubernetes did not ship with, added by a CRD. Frank's ArgoCD Applications, Rollouts and Tekton Pipelines are all CRs.)  (two-layer reconcile), native OIDC SSO via Authentik, Gitea-backed Job Templates |
| **hermes (Nous Research)** | Terminal-native agent CLI in a dedicated ` agent-shell-base ` pod on gpu-1 — BYOK (Bring Your Own Key) (**Bring Your Own Key**A tool that expects you to supply an API key for a model provider rather than brokering access itself. On Frank's agent shells these need a shim to re-export the key into login shells, because sshd scrubs container env.)  to LiteLLM (provider pinned via ` config.yaml ` mapping), profile.d shim defeating the sshd env-scrub, SSH+Mosh on ` 192.168.55.226 ` |
| **metrics-server** | Aggregated resource Metrics API (` metrics.k8s.io `) on Talos — restores ` kubectl top nodes/pods ` and unblocks CPU/memory HPA (HorizontalPodAutoscaler) (**HorizontalPodAutoscaler**The controller that adds and removes pod replicas based on a metric. It needs a metrics API to be serving, which is a separate component from the monitoring stack.) ; ` --kubelet-insecure-tls ` for self-signed kubelet certs. Custom/external metrics deferred to a VM-backed prometheus-adapter |



## Cluster State



Four zones, lettered A to D. **Zone A** is management and has no row here on purpose: it lives outside the cluster, so it has no node to appear as. The other three are the cluster.



| Node | Zone | Role | Hardware |
| --- | --- | --- | --- |
| mini-1/2/3 | B — Core HA | Control-plane + Worker | Intel Ultra 5, 64GB RAM, 1TB NVMe, Arc iGPU |
| gpu-1 | C — AI Compute | Worker | i9, 128GB RAM, RTX 5070 Ti (16GB GDDR7), 2x4TB SSD |
| pc-1 | D — Edge | Worker | Legacy desktop, 64GB SSD + 3x HDD |
| raspi-1/2 | D — Edge | Worker | Raspberry Pi 4, 32GB SD |



## Verify this page against a live cluster



Everything in the Technology → Capability Map arrives as an ArgoCD Application, so the table is checkable rather than a claim you have to take on trust. The ` kubectl ` lines below want a kubeconfig for a cluster; the ` grep ` lines want a clone of [derio-net/frank](https://github.com/derio-net/frank) and run from its root.



**Every number below was captured on 2026-07-29** and will have moved since. The command is the durable part; the digits are a sample so you can tell whether you typed it right. Start with the count:



```console
$ kubectl -n argocd get applications --no-headers | wc -l
      69
```



Sixty-eight of those are templated by the App-of-Apps. The sixty-ninth is ` root ` itself, applied by hand once at bootstrap, because at that moment nothing exists yet to apply it:



```console
$ grep -l 'kind: Application' apps/root/templates/*.yaml | wc -l
      68
```



That arithmetic is the whole GitOps claim in one line. When the two numbers stop agreeing, something on the cluster was made outside git.



Existing and converged are different questions, so ask the second one separately:



```console
$ kubectl -n argocd get applications \
    -o jsonpath='{range .items[*]}{.status.sync.status}{" "}{.status.health.status}{"\n"}{end}' \
  | sort | uniq -c | sort -rn
  62 Synced Healthy
   5 OutOfSync Healthy
   1 Synced Suspended
   1 OutOfSync Missing
```



Sixty-two green out of sixty-nine, on an ordinary afternoon. ` Suspended ` is a canary parked at a manual promotion gate — Argo Rollouts doing its job, not a fault. ` OutOfSync ` is live state disagreeing with git. Neither is an emergency, and a cluster reporting all green all the time is usually one nobody is asking hard questions of.



The roadmap at the top is numbered by published post. The layer codes that plan filenames and commit messages use are a separate registry:



```console
$ grep -c '^  - code:' docs/layers.yaml
22
```



Twenty-one numbered capability layers, plus ` repo ` for meta-work that is not a cluster capability at all. The two sequences match through Layer 17 and diverge after it, because layers stopped shipping in the order they were planned. If you are looking for the post that covers a layer, follow the roadmap; if you are looking for the plan that built it, follow the code.



## Missteps



Three forks where the obvious choice was the wrong one.



| The fork | Why the obvious branch was wrong | What Frank does now | Evidence |
| --- | --- | --- | --- |
| **Maintain the index by hand, or derive it from the pages** | Hand-maintained indexes drift silently — nothing fails when you forget, and reordering means editing every entry | Layer registry in ` docs/layers.yaml `; ` {{< roadmap >}} ` and ` {{< series-index >}} ` derive their cards from page frontmatter | ` d7678b9e `, ` cfb7dd1e ` |
| **One series or two** | Build narrative and runbook have different readers and different half-lives. A build post is a dated story and should age; a runbook has to stay true | Split into ` building/ ` and ` operating/ `, cross-linked at render time | ` 7f5ff73f `, ` fc274975 ` |
| **` relref ` or a plain path for cross-post links** | ` relref ` validates targets at build time, but a missing ` /docs/ ` prefix is not a warning — it is ` REF_NOT_FOUND ` and a dead ` build-pages ` job | Prefix every ` relref ` with the full section path; escape any shortcode you are only quoting as ` {{< ` … ` >}} ` | ` 2840cce7 ` |
