No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-16 15:01:59 +00:00
_archive add kube-prometheues-stack and archive lgtm-stack 2026-08-11 22:44:47 +02:00
applications chore(deps): update ghcr.io/immich-app/immich-server docker tag to v3.2.2 2026-09-15 20:32:31 +00:00
batch chore(deps): update ghcr.io/renovatebot/charts/renovate docker tag to v46.301.0 2026-09-16 15:01:59 +00:00
clusters chore(deps): update fluxcd/flux2 from v2.9.4 to v2.9.5 2026-08-31 18:01:06 +00:00
infrastructure chore(deps): update helm release traefik from 41.5.0 to 41.6.0 2026-09-16 14:01:27 +00:00
shared refactor: colocate base and overlays under each component 2026-08-12 03:12:50 +02:00
templates oci: scw cleanup 2026-08-15 05:25:25 +02:00
traefik-pve-forward fixes 2026-08-16 12:49:23 +02:00
.gitignore fixes 2026-08-09 21:25:29 +02:00
AGENTS.md refactor: config halves of each component 2026-08-12 03:56:03 +02:00
CLAUDE.md refactor: config halves of each component 2026-08-12 03:56:03 +02:00
LICENSE add MIT LICENSE for going public 2026-05-10 19:21:33 +02:00
README.md oci: scw cleanup 2026-08-15 05:25:25 +02:00
renovate.json renovate: ignore _archive, pin both desec images 2026-08-12 00:27:29 +02:00
services.md add kube-prometheues-stack and archive lgtm-stack 2026-08-11 22:44:47 +02:00
TODO.md empty todo 2026-08-12 01:03:53 +02:00

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 on 10.50.0.21, one Gateway each), upstream Gateway API CRDs, External DNS to deSEC and Technitium, cert-manager with Let's Encrypt + deSEC DNS-01 webhook and a single *.webmatix.dev wildcard.
  • 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 the traefik-pve LXC.

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 Secret is either an ExternalSecret resolving against OpenBao, or a placeholder template. The *-config.json.example files 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.