You've made the architecture decision (k3s HA vs Single Node: Best Architecture for Your Homelab?), planned your IPs (Homelab Kubernetes IP Planning: The Step Nobody Mentions), installed k3s (Why k3s? Choosing and Installing k3s for Your Homelab), and taken the kube-vip deep dive (kube-vip for k3s Homelab: Control Plane HA with a Virtual IP). Four posts, one running HA cluster. Now what?
Four more components in a specific, non-negotiable order — and a fundamental shift from "I run commands" to "git push deploys my infrastructure."
This post is the roadmap. It connects the foundation you've built to everything that comes next.
Most homelab Kubernetes journeys stall after install. The cluster runs, but it's not production — no TLS, no GitOps, no secrets management, no storage, no databases. The gap between "I installed k3s" and "I have a production homelab" is wide and poorly mapped. Individual tutorials exist for each component, but nobody explains how they fit together as a system — and the order you install them determines whether they work or fail mysteriously.
I know because I got the order wrong. Multiple times. On my own cluster. Deployed cert-manager before Sealed Secrets and spent an evening wondering why DNS-01 challenges wouldn't work. Skipped MetalLB and stared at <pending> LoadBalancer IPs for an hour. These aren't hypothetical failure modes — they're things I actually did, fixed, and documented in the homelab-k8s repo.
By the end of this post, you'll understand the full k3s bootstrap order — the six-component chain (kube-vip → Sealed Secrets → cert-manager → MetalLB → Traefik → ArgoCD), WHY each step depends on the one before it, what GitOps and ArgoCD actually are (conceptually — we implement ArgoCD in Pillar 2), and the curriculum arc that takes your HA cluster to 18+ production apps deployed via git push. This is the post that connects Section 1 to everything that follows. This is the architecture of the curriculum itself.
Key Takeaways
- A production k3s cluster needs six infrastructure components deployed in a specific dependency chain: kube-vip → Sealed Secrets → cert-manager → MetalLB → Traefik → ArgoCD. Each depends on the one before it. Deploy out of order and you get cascading failures that look like bugs but are dependency violations.
- GitOps means your git repository is the single source of truth for your cluster. ArgoCD watches the repo and ensures the cluster matches it.
git push= deploy.git revert= rollback. No more "which machine has the latest version of this YAML?"- 77% of organizations have adopted GitOps (CNCF Annual Survey, January 2026). It's not an enterprise pattern — it's the standard pattern. A single-person homelab with a public git repo IS a GitOps operation.
- ArgoCD is always last in the chain — it needs everything else to exist before it can function. The bootstrap paradox: every component before ArgoCD exists to make ArgoCD possible. After ArgoCD is connected, you never run
kubectl applyagain.
The Six-Component Bootstrap — What We're Building and Why
A production homelab cluster needs six infrastructure components deployed before you can run a single application. Each one solves a specific problem that, if left unsolved, makes your cluster either insecure, unreachable, or unmanageable. With 82% of Kubernetes users now running it in production (CNCF Annual Survey, January 2026), the bootstrap discipline described here is what separates production deployments from toy clusters.
The six components form a dependency chain where each exists to make the next one possible. This isn't a shopping list — it's a sequence.
| Step | Component | Problem It Solves | What Happens Without It | Depends On | Section |
|---|---|---|---|---|---|
| 1 | kube-vip | Control plane single point of failure | kubectl dies with one node; cluster unstable during subsequent deployments | (none) | Section 1 |
| 2 | Sealed Secrets | Can't commit secrets to public git repo | No safe way to store credentials in git; cert-manager has nowhere to put DNS-01 keys | kube-vip | Section 2 |
| 3 | cert-manager | Manual TLS certificate management | Expired certs, no HTTPS; Traefik starts with no TLS certs to serve | Sealed Secrets | Section 2 |
| 4 | MetalLB | LoadBalancer services stay <pending> | No external IPs; Traefik unreachable, ArgoCD UI unreachable | cert-manager | Section 3 |
| 5 | Traefik | No HTTP routing to applications | Every app needs its own port; ArgoCD UI unreachable outside cluster | MetalLB, cert-manager | Section 3 |
| 6 | ArgoCD | Manual kubectl apply for everything | No history, no rollback, no GitOps; manual kubectl apply forever | Traefik | Pillar 2 |
Why six and not five or seven: These six are the minimum set for a production-style homelab. Each solves a problem that every cluster operator hits, and each is the simplest tool in its category that works at homelab scale:
- kube-vip: ARP-based VIP is the simplest HA mechanism for homelab — no external load balancer, no hardware dependency. Runs as a Kubernetes DaemonSet rather than a system-level service like keepalived.
- Sealed Secrets: Encrypts secrets so they live in git alongside the resources that use them — single source of truth, no external vault. Zero external dependencies; chosen over External Secrets Operator for homelab simplicity.
- cert-manager: Automates Let's Encrypt DNS-01 challenges — wildcard certs, no open ports, automatic renewal 30 days before expiry. Integrates directly with Traefik's IngressRoute resources.
- MetalLB: L2 mode assigns real IPs from your subnet to LoadBalancer services. Chosen for its IPAddressPool CRD — finer control over which services get which IPs than kube-vip's built-in LoadBalancer mode.
- Traefik: Single ingress point with automatic TLS termination, path-based routing, and middleware chaining. k3s bundles Traefik — it's already installed and running, zero additional bootstrap overhead. Chosen over NGINX Ingress for that reason.
- ArgoCD: Makes
git pushthe only deployment mechanism. Chosen over Flux for its web UI — a visual dashboard showing "all green" for a cluster you check on once a day.
Six is the number that works. Fewer and you're doing manual work forever. More — monitoring stack, service mesh, logging pipeline — and you're optimizing before you're operational. These six get you to production. Everything else is optimization.
When each component gets its own deep-dive: This post introduces each conceptually. Sections 2–6 implement them with full tutorials. The gap between "understand the chain" (this post) and "build each link" (every post after this) is intentional — you don't need implementation details to understand the architecture. This post is the map. The next posts are the trails.
The Dependency Chain — Why Order Is Non-Negotiable
The bootstrap order isn't a convention — it's enforced by hard dependency relationships. Each component needs something the previous component provides. The table above shows the chain. Here's what violating it looks like in practice.
The most common failure — cert-manager before Sealed Secrets: This is the dependency violation that bites everyone. cert-manager needs DNS-01 challenge credentials to prove you own your domain to Let's Encrypt. Those credentials — Route53 API key, Cloudflare API token — are secrets. If you deploy cert-manager before Sealed Secrets, you have three options and none are clean:
- Commit the credentials in plaintext. DO NOT DO THIS. Even if your repo is private.
- Skip DNS-01 and use HTTP-01 challenges. No wildcard certs, requires open port 80, doesn't work behind NAT for many homelab setups.
- Create the secret directly with
kubectl create secret. This works, but now the secret lives outside git — violating the GitOps pattern and meaning you'll have to manually recreate it if the cluster is rebuilt.
I did option 3 first. Then I rebuilt the cluster — because of course I did, that's how homelabs work — and had to recreate every secret from memory. That's the lesson: the ordering isn't arbitrary. It's a chain of "this must exist before that can work." The clean path: Sealed Secrets first, then cert-manager with its credentials stored as a SealedSecret.
Why ArgoCD is always last: ArgoCD is the bootstrap paradox. Every component before it exists to make ArgoCD possible. ArgoCD, once running, manages ALL of them going forward — including future updates to itself. The handoff: your last manual kubectl apply targets ArgoCD. After that, git push is the only deployment mechanism. The cluster manages itself.
What "managed by ArgoCD" means: When ArgoCD adopts a resource, it continuously compares the live state in the cluster with the desired state in git. If they differ, ArgoCD corrects the cluster to match git. This means: if you manually kubectl edit a resource, ArgoCD reverts your change within 3 minutes (the default sync interval). If you git push a change, ArgoCD applies it automatically. git becomes the ONLY way to change the cluster. This is terrifying at first — "what if I need to fix something RIGHT NOW?" — and liberating once you trust it. Every change is a commit with a message. You can see exactly what changed and when.
GitOps — What It Is, Why It Matters, and Why You Want It
GitOps means your git repository is the single source of truth for your entire cluster. Every application, every configuration, every secret (encrypted) — defined as code in git. ArgoCD watches the repo and ensures the cluster matches. The workflow: edit YAML, git commit, git push. ArgoCD sees the change, compares it to the live cluster, and applies the diff. That's it. That's the entire operational model.
| Scenario | Manual (kubectl apply) | GitOps (ArgoCD) |
|---|---|---|
| Deploy an app | kubectl apply -f from your laptop, hope the YAML is current | Edit YAML, git push, ArgoCD deploys |
| Update an app | kubectl edit (no record), or find the file, edit, kubectl apply again | Edit YAML, git push, ArgoCD syncs |
| Roll back | Hope you kept a backup of the old YAML. You did, right? | git revert, git push, ArgoCD rolls back |
| Know what changed | Guess. Check bash history on three different machines. | git log — every change with timestamp and message |
| Rebuild from scratch | Panic. Try to remember everything you installed. | Clone repo, run bootstrap, ArgoCD deploys everything |
| Share with someone | "Here's a folder of YAML files... I think these are the right versions." | "Here's the repo. Clone it. It's the exact state of my cluster." |
No more kubectl apply against the cluster. No more "which machine has the latest version of this YAML?" No more "I changed something six months ago and now it's broken and I don't know what I changed." Every change is a commit. Every commit has a message. git log is your audit trail. git revert is your rollback.
And here's the part that matters most for a solo operator: if you DO run kubectl apply manually out of habit or desperation, ArgoCD detects the drift and reverts the cluster back to match git within 3 minutes. Manual changes don't stick — git wins every time. This is a safety net that prevents the configuration drift that kills homelabs.
The homelab-specific case for GitOps: You're one person. You don't have a team. You don't have change control boards. So why GitOps? Because YOU are the team — and "you from six months ago" is a different person who made decisions "you right now" don't remember. GitOps is the external memory for your cluster. When something breaks at 11pm and you're tired and you don't remember changing anything, git log --since='7 days ago' tells you exactly what changed. That alone is worth the price of admission.
77% of organizations have adopted GitOps, with average containers per organization reaching 2,341 (CNCF Annual Survey, January 2026). It's not an enterprise pattern — it's the standard pattern. And it scales down: a single-person homelab with a public git repo IS a GitOps operation. The principles are the same. The tools are the same. The skills transfer directly to a job.
When GitOps starts in THIS cluster: We don't implement ArgoCD until we reach the end of the bootstrap articles. That's not because GitOps is an afterthought — it's because ArgoCD needs everything to exist first. The bootstrap paradox is real. We build the platform, THEN we hand it to GitOps. The handoff happens once, and it's the most satisfying moment in the entire curriculum.
ArgoCD — The Controller That Takes Over Your Cluster
ArgoCD is a Kubernetes controller that watches a git repository and ensures the cluster matches what's defined there. It runs inside your cluster, checks the repo every 3 minutes (default), and if it finds a difference — a missing deployment, a changed config value, a deleted service — it corrects the cluster to match git. ArgoCD graduated from CNCF Incubator in December 2022 and now sits at 23,100+ GitHub stars with the latest release at v3.4.3 (GitHub, May 2026). It's mature, it's battle-tested, and it's the standard GitOps controller.
ArgoCD also provides a web UI that visualizes every application, its health status, and its sync state. Green = cluster matches git. Yellow = something's out of sync. Red = something's broken. This UI becomes your cluster dashboard — you check it instead of running kubectl get pods across ten namespaces.
Green squares are a hell of a drug. When all your apps show "Synced" and "Healthy," you have confidence the cluster is working. When something turns yellow or red, you know immediately — no need to run kubectl get pods --all-namespaces | grep -v Running. The UI is not a gimmick. It's operational awareness for a cluster you check on once a day.
What ArgoCD manages: Everything. Kubernetes manifests, Helm charts, Kustomize overlays, raw YAML, JSON. If it can be expressed as a file in git, ArgoCD can deploy it. In the homelab-k8s repo, ArgoCD manages: infrastructure operators (CloudNativePG, cert-manager, Longhorn), platform services (Authentik, databases), application workloads (Mealie, n8n, Manyfold, Open WebUI), and even itself — ArgoCD watches its own configuration in git and self-updates.
The App-of-Apps pattern: ArgoCD has one "root app" — app-of-apps.yaml — that points at apps/manifests/ in the repo. Every .yaml file in that directory is auto-discovered as a separate ArgoCD Application. Each of those manifest files defines a single app and points source.path to a specific directory under apps/ — for example, the cert-manager.yaml manifest in apps/manifests/ points at apps/cert-manager/, which contains the actual cert-manager resources. Adding a new app is: create its directory under apps/ with the Kubernetes resources, add a manifest file in apps/manifests/ pointing at that directory, then git push. The root app discovers the new manifest and ArgoCD deploys it. No ArgoCD configuration changes needed — just a file in apps/manifests/.
Why ArgoCD and not Flux: ArgoCD has a built-in web UI that let's you easily perform tasks within the cluster. Flux doesn't — it's CLI-only. For a solo operator, a visual dashboard showing "all my apps are green" is worth more than a CLI tool you forget to run. ArgoCD's sync waves feature maps directly to the bootstrap dependency chain (operators sync at wave -2, platform services at -1, workloads at 0). Flux can do this but requires more configuration. The homelab-k8s repo uses ArgoCD. This curriculum teaches ArgoCD. The choice is pragmatic, not religious — I use this, it works, here's why I picked it. For an objective comparison, Pillar 2 includes a full ArgoCD vs Flux breakdown.
The Curriculum Roadmap — Six Pillars, Seven Sections, One Cluster
The homelab Kubernetes curriculum is organized around six content pillars — thematic clusters that together form a complete production homelab. These pillars are delivered across seven curriculum sections in deployment order. Each pillar has a hub page and spoke posts. k3s itself sits at 33,100+ GitHub stars (GitHub, May 2026) — the foundation this entire curriculum is built on.
| Pillar | Topic | Posts | Status |
|---|---|---|---|
| Pillar 1: k3s HA the Right Way | kube-vip, Sealed Secrets, cert-manager, MetalLB, Traefik — the full bootstrap chain | 10 | 4 published, 1 scheduled, 1 drafted, 4 briefed |
| Pillar 2: ArgoCD App-of-Apps | GitOps controller, sync waves, Helm integration, auto-discover pattern | 6 | Planned |
| Pillar 3: Authentik SSO | Forward auth, OIDC, LDAP outpost, single sign-on for every app | 7 | Planned |
| Pillar 4: Kubernetes + Unraid | SMB CSI, Longhorn, storage tiers, backup strategy | 7 | Planned |
| Pillar 5: Database Operations | CloudNativePG, MariaDB operator, PgBouncer, PITR backups | 6 | Planned |
| Pillar 6: LLM Stack (Bonus) | LiteLLM, Open WebUI, SearXNG on k3s | 3 | Planned |
39 posts total. Every component runs with real configs in the homelab-k8s repo.
Pillars vs sections — how the curriculum is structured: Pillars are the topic clusters. Sections are the deployment order — the sequence you build things in. They don't map 1:1. Pillar 1 spans three curriculum sections because the bootstrap chain IS the deployment order for the foundation layer. Later pillars map to later sections; their exact sequencing is determined as we approach them. This is the map. The pillars are the territory.
The deployment arc — where the sections land:
| Section | Focus | Posts | Key Deliverable | Pillar |
|---|---|---|---|---|
| 1: Cluster Foundation | Architecture decisions, k3s install, kube-vip HA, GitOps primer | 5 | Running HA cluster with control plane VIP | Pillar 1 |
| 2: Security Foundations | Sealed Secrets, cert-manager | 2 | Encrypted secrets in git, automatic TLS via DNS-01 | Pillar 1 |
| 3: Networking | MetalLB, Traefik, IngressRoutes | 2 | LoadBalancer services, HTTP routing, TLS termination | Pillar 1 |
| 4: Storage | SMB CSI, Longhorn, storage tiers | TBD | Persistent storage from NAS + replicated block storage | Pillar 4 |
| 5: Databases | CloudNativePG, MariaDB operator, PgBouncer | TBD | HA Postgres + MariaDB with backups and PITR | Pillar 5 |
| 6: GitOps Core | ArgoCD install, App-of-Apps, first deployment | TBD | git push deploys everything — the handoff | Pillar 2 |
| 7: Application Deployments | Authentik SSO, LLM stack, 18+ production apps | TBD | Production homelab running real workloads | Pillars 3, 6 |
How sections connect: Section 2's Sealed Secrets encrypts the DNS-01 credentials Section 2's cert-manager needs. Section 3's Traefik provides the ingress Section 6's ArgoCD needs for its UI. Section 4's Longhorn provides persistent storage for Section 5's databases. Section 5's Postgres cluster is the backend for Section 7's applications. Sections aren't independent — they're layers in a stack, taught in deployment order.
Where you are now: You're at the end of Section 1. You have a running k3s cluster with kube-vip providing control plane HA. Starting with the next post, every article is hands-on implementation. Section 2 starts with Sealed Secrets: Committing Encrypted Secrets to a Public Kubernetes Repo.
The handoff moment: There's a specific moment that separates "manual cluster" from "GitOps cluster." It happens when you run the last manual kubectl apply -f app-of-apps.yaml — deploying ArgoCD, which then takes over management of the entire cluster. Before that moment, you're running kubectl apply manually. After it, git push is the only deployment mechanism. The curriculum builds toward this handoff — and it's surprisingly anticlimactic. One command. Then silence. git push. It's deployed.
The publishing cadence: Two posts per week (Tuesday + Thursday). Sections are written in batches of 4–6 modules before publishing begins. At two posts per week, you're building your cluster alongside the curriculum. The GitHub repo is always ahead of the blog — peek at future sections by browsing the repo, or follow along at publishing pace.
This post is the last "overview" post. From here forward: hands-on, real configs, real commands. The Production k3s HA In Your Homelab: The Complete Architecture pillar page is the reference. The spoke posts are the step-by-step. This post is the bridge — the map that shows where we've been and where we're going.
Frequently Asked Questions
Why can't I install everything at once with a script?
You can. Many people do. The problem is when something breaks — and it will — you won't know WHICH component failed or WHY, because you deployed six things simultaneously and the error messages are interleaved. Deploying one at a time, verifying each, means you know exactly which step broke and what changed. Scripts are for rebuilds after you understand the chain. Manual first. Automate later.
Do I need all six components? What if I skip MetalLB?
You can use NodePort instead of LoadBalancer services. You can use HTTP-01 challenges instead of cert-manager's DNS-01. You can keep secrets in a password manager instead of Sealed Secrets. But each skip adds manual work you'll do forever — renewing certs, looking up credentials, remembering port numbers. The six components are the minimum set for a cluster that runs itself. Skip what you want, but know what you're signing up for.
Why wait until Section 6 for ArgoCD? Can't I install it now?
ArgoCD needs a stable cluster, TLS certificates, a LoadBalancer IP, and an ingress route — all things that don't exist yet at the end of Section 1. You CAN install ArgoCD earlier, but you'll access it via kubectl port-forward (no ingress), with a self-signed cert (no cert-manager), and you'll configure it manually (no GitOps for the GitOps tool). It works. It's not production. The curriculum installs it at the right time — when everything it needs already exists.
Is this curriculum for k3s, or does it work with other Kubernetes distributions?
The concepts are universal. The specific commands, flags, and gotchas are k3s-focused. kube-vip, Sealed Secrets, cert-manager, MetalLB, Traefik, and ArgoCD work on any Kubernetes distribution. The IP planning, dependency chain, and GitOps patterns apply everywhere. But this curriculum uses k3s because it's the right tool for homelab — and I run it in production. If you're on kubeadm or Talos, the concepts transfer; the commands don't.
What if I get stuck? What's the support path?
Three options: (1) The GitHub repo has working configs for every component — compare yours to the reference. (2) The repo's docs/ directory has troubleshooting guides for common failures (multipathd, Longhorn mounts, cert-manager issues). (3) r/homelab, r/kubernetes, and r/k3s — post your specific error, your config (sanitized), and what you've tried. The community is helpful. I hang out in all three.
Just Do Something
The bootstrap chain looks intimidating — six components in strict order, each depending on the one before it. But here's the thing: you don't need to understand all six before starting. You need to understand the NEXT one.
Install kube-vip. Verify it works. Move to Sealed Secrets. One step at a time. The sequence exists so you DON'T have to understand everything at once — it tells you what to do next. The curriculum is the path. Follow it.
"You can have excuses or results. YOU CAN'T HAVE BOTH."
Section 1 is done. You have a running HA cluster. Next up: Sealed Secrets — committing encrypted credentials to a public GitOps repo, because secrets belong in git when they're encrypted. Section 2 starts there. That's the first hands-on implementation post after this. The bridge from "here's the map" to "here's the first trail."
What do you think? Did I miss a dependency in the chain? Have you deployed these in a different order and had it work? Let me know in the comments — I'm genuinely curious what orders people land on, and whether the failure modes matched what I described.
And if you're following along: don't wait. The GitHub repo is public. The configs are real. The cluster is running. Go look. Then build yours.