Architecture Overview¶
High-Level Design¶
┌──────────────────────────────────────────┐
│ GitHub (this repo) │
│ master branch = truth │
└─────────────────┬────────────────────────┘
│ push
▼
┌───────────────────────────────────────────────────────────────────┐
│ K3s Cluster │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────────────────┐ │
│ │ k3s-m1 │ │ k3s-m2 │ │ k3s-oci-m3 (OCI) │ │
│ │ master │ │ master │ │ master (Tailscale VPN) │ │
│ └──────────┘ └──────────┘ └───────────────────────────┘ │
│ │
│ ┌──────────┐ │
│ │ hoarder │ (NAS — storage only) │
│ │ Unraid │ │
│ └──────────┘ │
│ │
│ ArgoCD watches GitHub → applies changes automatically │
└───────────────────────────────────────────────────────────────────┘
│
┌─────────┴──────────┐
▼ ▼
┌──────────────┐ ┌────────────────┐
│ Cloudflare │ │ Tailscale mesh │
│ DNS/Tunnels │ │ (server comms) │
└──────────────┘ └────────────────┘
Repository Layout¶
homelab/
├── kubernetes/ # All Kubernetes manifests
│ ├── bootstrap/ # One-time cluster bootstrap (Helmfile)
│ │ ├── helmfile.yaml # Installs GPG secret, repo key, ArgoCD, root app
│ │ └── values/ # Values for bootstrap releases
│ ├── apps/
│ │ ├── addons/argocd-apps/ # Master chart — generates all ArgoCD Application CRDs
│ │ │ ├── application.yaml # Root ArgoCD Application (applied by bootstrap)
│ │ │ ├── values.yaml # Sets cluster destination for all apps
│ │ │ └── templates/
│ │ │ ├── <namespace>/ # One .yaml per app, one folder per namespace
│ │ │ └── ...
│ │ └── <namespace>/<app>/ # Per-app Helm values
│ │ ├── values.yaml
│ │ └── values.sops.yaml # Encrypted secrets
│ └── charts/ # Ad-hoc Kubernetes resource charts
├── ansible/ # K3s provisioning and server management
│ ├── playbooks/
│ │ ├── k3s-cluster.yml # Main playbook — includes bootstrap tag
│ │ └── ...
│ ├── roles/
│ └── inventory/
├── terraform/ # External infrastructure
│ └── cloudflare/ # DNS, tunnels, WAF
├── docs/ # This documentation
└── .sops.yaml # SOPS encryption rules
Bootstrap vs GitOps¶
The cluster lifecycle has two phases:
Bootstrap (once) — run manually on a fresh cluster:
uv run ansible-playbook ansible/playbooks/k3s-cluster.yml # provision K3s
uv run ansible-playbook ansible/playbooks/k3s-cluster.yml --tags bootstrap # install ArgoCD
GitOps (ongoing) — ArgoCD takes over after bootstrap:
- A change is committed and pushed to
master - ArgoCD detects drift between the cluster state and the Git state
- ArgoCD applies the diff — creating, updating, or pruning resources
selfHeal: trueensures any manual in-cluster changes are reverted back to Git state
The argocd-apps chart is the root of the tree. It is itself an ArgoCD Application (defined in application.yaml) that generates all other Application CRDs from the templates/ directory.
Stack Summary¶
| Layer | Technology |
|---|---|
| Kubernetes distribution | K3s |
| GitOps | ArgoCD |
| Ingress | ingress-nginx |
| TLS | cert-manager + Let's Encrypt |
| Load balancer | MetalLB |
| Persistent storage | Longhorn |
| Secret encryption | SOPS + PGP |
| Provisioning | Ansible |
| External infra | Terraform |
| DNS & tunnels | Cloudflare |
| VPN mesh | Tailscale |