30.3 Kustomization: Deploying Kustomize Overlays via Flux
Right, so you’ve got your Git repository set up as your source of truth—your single, glorious, version-controlled system of record. But let’s be honest, you’re probably not deploying the exact same YAML to every environment. You need to change the number of replicas in production, or swap out a config map for staging. This is where Kustomize struts in, and Flux’s Kustomization resource is how you make it dance.
Think of a Kustomization (the Flux kind, capitalized, we’ll get to that) as the conductor of your deployment orchestra. It doesn’t hold the musical scores itself; it points to a directory in your Git repo that contains your kustomization.yaml (the Kustomize kind, lowercase, yes it’s confusing) and then it tells Flux, “Hey, go to this git repository, grab everything in this folder, run kustomize build on it, and apply the resulting YAML to the cluster.” It’s the crucial link between your fancy, overlayed manifests and the cluster they’re supposed to run on.
The Anatomy of a Flux Kustomization
Here’s a basic but fully functional example. You’ll apply this YAML to your cluster, and Flux will take it from there.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app-production
namespace: flux-system
spec:
interval: 5m0s # How often to reconcile from Git
path: ./apps/my-app/overlays/production # The path to the kustomization.yaml dir
prune: true # Holy crap, this is important. It garbage collects resources removed from Git.
sourceRef:
kind: GitRepository # References the GitRepository source you already created
name: my-infra-repo # The name of that source object
validation: client # Uses `kubectl` for server-dry-run validation before applying
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: my-app
namespace: production
Let’s break down the critical bits. The interval is your pull frequency; it’s how often Flux says, “Anything new for me?” The path is everything. This must point to the directory containing your kustomization.yaml file, not the file itself. prune: true is non-optional for any real usage—it ensures that if you delete a manifest from Git, Flux will delete it from the cluster. This is GitOps, after all. The sourceRef is how this Kustomization knows which Git repository to even look at.
Why Prune is Your Best Friend and Worst Enemy
I need to stop and make you look at prune: true again. This is the feature that makes GitOps so powerful and so terrifying. It means your cluster state exactly matches your Git state. Delete a file? Flux will delete the resource. It’s brilliant. It’s also a fantastic way to accidentally delete a critical, stateful service if you mess up your kustomization.yaml patches. Always, always use validation: client and test your changes in a non-production environment first. Flux’s power demands respect.
Taming the Overlay Beast
Your Git repo structure should be sane. A common and effective pattern is:
├── apps/
│ └── my-app/
│ ├── base/
│ │ ├── kustomization.yaml
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── overlays/
│ ├── staging/
│ │ ├── kustomization.yaml # patches replicas, uses staging configmap
│ │ └── patch_replicas.yaml
│ └── production/
│ ├── kustomization.yaml # patches replicas, ingress, resource limits
│ ├── patch_replicas.yaml
│ └── patch_resources.yaml
└── infrastructure/
└── redis/
└── ...
Your Flux Kustomization for production would point to ./apps/my-app/overlays/production. It will build that overlay, which in turn references the base (or another overlay), and the result is what gets deployed. This keeps your environment-specific changes isolated and obvious.
When Things Go Sideways: Dependency Management
Here’s a fun “gotcha.” You have a Kustomization that deploys a Namespace and another that deploys an Application in that namespace. What happens if Flux tries to apply the App before the Namespace is ready? It fails. Spectacularly.
This is where dependsOn comes in. You can explicitly tell one Kustomization to wait for another.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app-production
namespace: flux-system
spec:
interval: 5m0s
path: ./apps/my-app/overlays/production
prune: true
sourceRef:
kind: GitRepository
name: my-infra-repo
dependsOn:
- name: my-namespace-production # Wait for this Kustomization to be healthy first
This is crucial for bootstrapping infrastructure in the correct order. Without it, you’re just hoping the cluster is feeling cooperative that day. Don’t hope. Define your dependencies.
Drift Detection and Self-Healing
The real magic isn’t just the deployment. It’s the reconciliation loop. Every five minutes (or whatever your interval is), Flux will:
- Pull the latest code from your specified branch.
- Run
kustomize buildon the specified path. - Calculate a diff between the desired state (the build output) and the actual state of the cluster.
- Apply any changes necessary to make the cluster match Git.
If someone goes rogue and kubectl edit’s a deployment to add a “debug” sidecar, Flux will spot the drift on its next run and revert the change to whatever is defined in Git. The cluster defends itself from configuration drift. This is the core superpower you signed up for. Use it wisely.