Flux-managed Kubernetes homelab. Infrastructure and media stack deployed declaratively from Git.
This repo replaces the imperative deploy.sh approach from k8s-media-stack with Flux GitOps. Same Helm charts, same values, automated delivery.
┌──────────────────────────────────────────────────────────┐
│ Git push │
│ │ │
│ ▼ │
│ Flux (in-cluster) │
│ │ │
│ ├── infrastructure/ (Kustomization, wait) │
│ │ ├── MetalLB HelmRelease │
│ │ ├── NFS CSI HelmRelease │
│ │ └── Traefik HelmRelease │
│ │ │
│ └── apps/ (Kustomization, depends) │
│ ├── Plex HelmRelease │
│ ├── Sonarr HelmRelease │
│ ├── Radarr HelmRelease │
│ ├── Prowlarr HelmRelease │
│ ├── qBittorrent HelmRelease │
│ ├── Bazarr HelmRelease │
│ ├── Overseerr HelmRelease │
│ ├── Tautulli HelmRelease │
│ └── Homepage raw manifest │
└──────────────────────────────────────────────────────────┘
- Talos Kubernetes cluster running (k8s-deploy)
fluxCLI installed (install guide)kubectlconfigured- GitHub personal access token with
reposcope - NFS directories created on Synology (see k8s-media-stack)
export GITHUB_TOKEN=<your-token>
export GITHUB_USER=<your-username>
flux bootstrap github \
--owner=$GITHUB_USER \
--repository=k8s-gitops \
--branch=main \
--path=clusters/homelab \
--personalThis:
- Installs Flux components into the cluster (
flux-systemnamespace) - Creates a
GitRepositorysource pointing at this repo - Creates a
Kustomizationthat syncsclusters/homelab/ - Commits the Flux manifests back to the repo
After bootstrap, Flux picks up infrastructure.yaml and apps.yaml from clusters/homelab/ and reconciles everything.
k8s-gitops/
├── clusters/
│ └── homelab/
│ ├── flux-system/ # Auto-generated by flux bootstrap
│ ├── infrastructure.yaml # Flux Kustomization → infrastructure/
│ └── apps.yaml # Flux Kustomization → apps/ (depends on infra)
├── infrastructure/
│ ├── kustomization.yaml # Native kustomize resource list
│ ├── sources/
│ │ └── helm-repositories.yaml # HelmRepository CRDs
│ ├── metallb/
│ │ ├── namespace.yaml
│ │ ├── helmrelease.yaml
│ │ └── config.yaml # IPAddressPool + L2Advertisement
│ ├── nfs-csi/
│ │ ├── helmrelease.yaml
│ │ └── storageclass.yaml
│ └── traefik/
│ ├── namespace.yaml
│ └── helmrelease.yaml
└── apps/
├── kustomization.yaml # Native kustomize resource list
├── media/
│ ├── namespace.yaml
│ ├── storage.yaml # Shared NFS PV/PVC
│ ├── plex.yaml # HelmRelease (LoadBalancer)
│ ├── sonarr.yaml # HelmRelease (Ingress)
│ ├── radarr.yaml
│ ├── prowlarr.yaml
│ ├── qbittorrent.yaml # HelmRelease (Ingress + LoadBalancer)
│ ├── bazarr.yaml
│ ├── overseerr.yaml
│ └── tautulli.yaml
└── dashboard/
└── homepage.yaml # Raw manifest (Deployment, Service, Ingress, ConfigMap)
Flux Kustomizations enforce ordering:
- infrastructure — deploys MetalLB, NFS CSI, Traefik. Waits for health checks.
- apps — deploys media apps. Only starts after infrastructure is healthy.
Within each layer, Flux applies resources in the order listed in kustomization.yaml.
Edit the HelmRelease values, push to main. Flux reconciles within 30 minutes (or on demand):
flux reconcile kustomization apps --with-sourceflux get kustomizations
flux get helmreleases -Aflux suspend helmrelease sonarr -n mediaResume later:
flux resume helmrelease sonarr -n mediaflux reconcile source git flux-systemBefore deploying, update these values for your environment:
| File | What to change |
|---|---|
infrastructure/metallb/config.yaml |
IP address pool range |
infrastructure/nfs-csi/storageclass.yaml |
NAS IP and NFS share path |
infrastructure/traefik/helmrelease.yaml |
Traefik LoadBalancer IP |
apps/media/storage.yaml |
NAS IP and NFS data path |
apps/media/plex.yaml |
Plex LoadBalancer IP, timezone |
apps/media/qbittorrent.yaml |
qBittorrent LoadBalancer IP |
| All app HelmReleases | TZ environment variable |
This repo is a 1:1 port of k8s-media-stack. Each helm install from deploy.sh maps to a HelmRelease CRD. Same charts, same values, automated delivery.
| deploy.sh | k8s-gitops |
|---|---|
helm install metallb ... |
infrastructure/metallb/helmrelease.yaml |
helm install traefik ... -f traefik-values.yaml |
infrastructure/traefik/helmrelease.yaml (values inline) |
helm install sonarr bjw-s/app-template -f values.yaml |
apps/media/sonarr.yaml (values inline) |
kubectl apply -f metallb-config.yaml |
infrastructure/metallb/config.yaml |
kubectl apply -f homepage.yaml |
apps/dashboard/homepage.yaml |