# A Tour of My Kubernetes Homelab GitOps Repo
Table of Contents
One repo to rule the cluster
Everything in my Kubernetes homelab lives in a single git repo and gets deployed by Argo CD.
The hardware
Nothing fancy here — lil PCs:
- An Intel NUC with an N100
- An old Lenovo ThinkCentre M920q I picked up on ebay lol
Both run Proxmox, and the Kubernetes nodes are Talos Linux VMs on top.
The repo
The repo is laid out like this:
gitops/├── root.yaml # The app-of-apps entry point├── apps/ # Argo CD Application definitions├── manifests/ # Kustomize dirs + Helm values per app└── charts/ # My own local Helm chartsApp of apps
The whole thing hangs off a single root Application that points at the gitops/apps directory. Every YAML file in there is another Argo CD Application, so adding something new to the cluster is just dropping a file in that folder and pushing.
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: root namespace: argocdspec: project: default source: repoURL: git@github.com:digitalsoba/homelab.git targetRevision: HEAD path: gitops/apps destination: server: https://kubernetes.default.svc namespace: argocd syncPolicy: automated: prune: true selfHeal: trueEvery app runs with prune: true and selfHeal: true, so drift gets stomped out automatically. Argo CD even manages itself — there’s an argocd app pointing at a kustomize directory with patches for OIDC login, RBAC, and running the server in insecure mode behind the gateway.
The infrastructure layer
The addons app is a folder of Argo CD Applications for all the cluster plumbing:
- MetalLB — LoadBalancer IPs on bare metal, handing out addresses from a small pool on my LAN via L2 advertisement
- kgateway — my Gateway API implementation. One shared
Gateway, and each app attaches its ownListenerSet+HTTPRoute - cert-manager — Let’s Encrypt certs via Cloudflare DNS01, with Gateway API and ListenerSet support enabled
- external-dns — watches HTTPRoutes/Ingresses (including ones attached via ListenerSets) and creates the Cloudflare DNS records for me
- external-secrets — syncs secrets from Infisical into the cluster
- CloudNativePG — Postgres operator, so every app gets a proper database instead of a SQLite file in a PVC
- Storage — local-path-provisioner for node-local volumes, plus csi-driver-nfs pointed at my Synology NAS for shared storage
- The little helpers — metrics-server, descheduler, and Reloader to bounce pods when their ConfigMaps/Secrets change
No secrets in git
Nothing sensitive lives in the repo. A ClusterSecretStore connects external-secrets to my Infisical project, and each app declares an ExternalSecret for what it needs.
The giant CRD problem
A dedicated crds app exists purely because some CRDs (kgateway’s gatewayparameters is ~738KB 😅) blow past the 262144-byte limit of the client-side last-applied-configuration annotation and break the sync. That app applies Gateway API, kgateway, and Prometheus Operator CRDs with per-resource Server-Side Apply, pinned to the same versions the addon charts deploy.
The actual apps
All this plumbing exists so I can run a handful of self-hosted apps:
- Forgejo — private git hosting, backed by a CNPG Postgres cluster
- Pocket ID — lightweight OIDC provider with passkey login; Argo CD and Miniflux both authenticate through it
- Miniflux — RSS reader, deployed from my own local Helm chart
- Homepage — dashboard with widgets, including one talking to the Argo CD API through a dedicated service account
Favorite trick: one IP for HTTPS and git-over-SSH
Forgejo needs port 22 for git pushes, but HTTPS goes through the shared kgateway Gateway. MetalLB’s allow-shared-ip annotation lets both Services claim the same LoadBalancer IP:
# On the Gateway (ports 80/443)metallb.io/loadBalancerIPs: 192.0.2.10metallb.io/allow-shared-ip: misoshoup-forge
# On the forgejo-ssh Service (port 22)metallb.io/loadBalancerIPs: 192.0.2.10metallb.io/allow-shared-ip: misoshoup-forgeSince the port sets don’t collide, git.misoshoup.com resolves to a single IP serving both the web UI and SSH. One hostname, one DNS record, zero weirdness in my git remotes.
Multi-source apps for upstream charts
Forgejo also shows off a nice Argo CD feature: multi-source Applications. The chart comes from Forgejo’s OCI registry, but the values file lives in my repo, pulled in via a $values ref:
sources: - repoURL: code.forgejo.org/forgejo-helm chart: forgejo targetRevision: 17.1.1 helm: valueFiles: - $values/gitops/manifests/forgejo/values.yaml - repoURL: git@github.com:digitalsoba/homelab.git path: gitops/manifests/forgejo targetRevision: HEAD ref: valuesThe second source also carries supplemental manifests — the CNPG Cluster, the ExternalSecret for admin credentials, and the Gateway API routes.
Rolling my own chart
For Miniflux I skipped upstream charts and wrote a small one in gitops/charts/miniflux. It bundles the Deployment, a CNPG Postgres cluster (the generated connection URI is injected as DATABASE_URL), an ExternalSecret for the admin bootstrap credentials, and the ListenerSet + HTTPRoute for exposure. OIDC against Pocket ID is a few lines of values:
oidc: enabled: true discoveryEndpoint: https://auth.misoshoup.com providerName: Pocket IDArgo CD renders the chart straight from git — no chart registry needed.
Whats Next?
- Monitoring?