| Filename | Latest commit message | Latest commit date |
|---|---|---|
| _archive | ||
| applications | ||
| batch | ||
| clusters | ||
| infrastructure | ||
| shared | ||
| templates | ||
| traefik-pve-forward | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| LICENSE | ||
| README.md | ||
| renovate.json | ||
| services.md | ||
| TODO.md | ||
flux
FluxCD GitOps tree for my single Kubernetes cluster (talos-prod). Public so it can serve as a working reference.
This repo is what Flux reconciles. Everything inside the cluster (operators, applications, scheduled jobs, ingress for off-cluster services) is defined here as YAML and deployed by pushing to the branch Flux watches. There is no application code, no build step, no test suite.
What runs here
The cluster bootstraps in layers under clusters/talos-prod/. Roughly:
- Networking and routing: MetalLB (L2 LB), Traefik as the Gateway API implementation (two instances: internal on
10.50.0.20, external on10.50.0.21, oneGatewayeach), upstream Gateway API CRDs, External DNS to deSEC and Technitium, cert-manager with Let's Encrypt + deSEC DNS-01 webhook and a single*.webmatix.devwildcard. - Storage: Longhorn for distributed block storage, CSI NFS for shared file storage.
- Data: CloudNativePG (Postgres operator with Barman S3 backups), Redis.
- Identity and secrets: Authentik (OIDC/SAML/LDAP), External Secrets Operator backed by OpenBao (off-cluster), kubernetes-reflector for cross-namespace secret mirroring.
- Observability: kube-prometheus-stack (Prometheus operator, Prometheus, Alertmanager, node-exporter, Grafana) on a local Longhorn volume, plus kube-state-metrics, metrics-server, and the Prometheus operator CRDs as a separate release. Metrics and alerting only: there is no log or trace store.
- Document processing: Gotenberg (PDF), Apache Tika (extraction/OCR), used by Paperless.
- Integrations: a scoped ServiceAccount token (pushed to OpenBao) that lets an off-cluster Home Assistant read and control the cluster.
- Applications: Immich, Paperless-NGX, Collabora Online, Umami, IT-Tools, BentoPDF, Vaultwarden, Woodpecker CI, traefik-whoami.
- Batch: IMAP backup CronJob (hourly), Renovate (every 30 min).
- External-service ingress: SNI-passthrough
TLSRoutes on the external Traefik that forward off-cluster Forgejo, Jellyfin, and Seerr to thetraefik-pveLXC.
The full catalogue with namespaces and one-line descriptions lives in services.md.
This repo vs. the homelab repo
Two repos, clean split:
- homelab: everything outside and underneath the cluster: Proxmox VMs, Talos config, OpenTofu that builds the cluster end-to-end, Helm chart sources I maintain, Docker images, host-level scripts. Bootstraps Flux against this repo.
- This repo (
flux): everything inside the cluster, reconciled by Flux. Once the homelab bootstrap finishes, day-2 changes happen here.
The one Helm chart of mine referenced from this repo, cnpg-cluster-chart, is sourced from helm-charts/ in the homelab repo and published to a private OCI registry on my Forgejo instance (git.webmatix.dev). This repo only consumes it via OCIRepository + HelmRelease.
Layout
clusters/talos-prod/ Flux Kustomization roots, the entry point Flux reconciles
infrastructure/ operators and their config overlays
applications/ self-hosted apps (immich, paperless, etc.)
batch/ scheduled jobs (imap-backup, renovate)
shared/ Kustomize components: Traefik middlewares, one copy per namespace
traefik-pve-forward/ SNI-passthrough ingress to the off-cluster traefik-pve LXC
templates/ legacy raw CNPG manifests, reference only
services.md service catalogue: every namespace, one line each
Every component owns its manifests and its overlays in one directory: <category>/<thing>/base/ for the manifests, <category>/<thing>/talos-prod/ for the per-cluster overlay. Most overlays carry no patches and just re-reference their base with resources: [../base]. An operator's configuration half follows the same shape one directory over: <thing>/config/ for the shared manifests, <thing>/talos-prod/config/ for the per-cluster overlay. Traefik and its config are the exception: one base, consumed twice, patched into an internal and an external instance, each with its own config. Each directory has its own README with the details.
On AI assistance
I use AI as a research and coding tool. The boring stuff (boilerplate YAML, looking up CLI flags, repetitive Terraform, formatting tables, and documentation, this README included) is AI-assisted. I hate writing docs and basically never had any until AI made it cheap enough to bother. Architecture choices, the stack, debugging when things break, anything operational, and every load-bearing decision in here are mine. If something works well, I picked it on purpose. If something is wrong, that's also on me.
I'm calling it out because it's the honest thing to do.
Caveats
- It's a single-cluster homelab GitOps tree. Hostnames, IPs, S3 bucket names, and OpenBao secret paths are all tuned to my setup. Cloning this won't reconcile against your cluster without significant rework.
- Real secret material is not here. Every
Secretis either anExternalSecretresolving against OpenBao, or a placeholder template. The*-config.json.examplefiles show the shape but contain no live values. - Host TLS, S3 backup destinations, and DNS provider tokens all assume my domain (
webmatix.dev) and my deSEC / Technitium accounts. Swap these before reusing. _archive/holds components that ran here and were removed. Nothing in it is reconciled.
License
MIT, see LICENSE. Lift whatever's useful, no permission needed.
Contact
matthias@webmatix.dev, or find me on Codeberg at @webmatix.