Flux is designed to make Kubernetes deployments repeatable and manageable, and a core part of that is being able to customize your deployments for different environments without modifying your Git repository. Substituting variables in your Flux manifests, particularly using ConfigMaps, is a fundamental technique for achieving this.
Let’s see how this works in practice. Imagine you have a Deployment manifest in your Git repository that needs a different database password or API endpoint depending on whether it’s running in development or production. Instead of having two separate Deployment files, you can use a single template and inject the environment-specific values.
Here’s a simplified example of a kustomization.yaml file that uses ConfigMaps to substitute variables.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
configMapGenerator:
- name: app-config
literals:
- DB_HOST=db.example.com
- DB_PORT=5432
- API_KEY=this-is-a-secret-for-dev
- name: app-config-prod
literals:
- DB_HOST=prod-db.example.com
- API_KEY=this-is-the-real-secret
# This is how you selectively apply a ConfigMap for a specific environment.
# In a real Flux setup, this would be managed by a source controller or a branch strategy.
# For this example, we'll assume 'prod' is a label selector or a specific namespace.
namespace: prod
patchesStrategicMerge:
- deployment-patch.yaml
And here’s the deployment.yaml that will be modified:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
image: my-app:latest
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config # This will be substituted
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: API_KEY
valueFrom:
configMapKeyRef:
name: app-config
key: API_KEY
The patchesStrategicMerge section would then contain a deployment-patch.yaml that overrides the ConfigMap name if running in production.
# deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
env:
# This patch targets the 'prod' environment where app-config-prod is used.
# In a real Flux scenario, this patch might be conditionally applied
# based on a Git branch, a Helm release's values, or a Flux Kustomization's
# namespace or label selectors.
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config-prod # Override for production
key: DB_HOST
- name: API_KEY
valueFrom:
configMapKeyRef:
name: app-config-prod # Override for production
key: API_KEY
When Flux processes this Kustomization, it first generates the app-config ConfigMap with the default values. If a condition for production is met (e.g., processing a prod branch or targeting the prod namespace), it will also generate app-config-prod and then apply the deployment-patch.yaml. This patch tells Kubernetes to use app-config-prod for the DB_HOST and API_KEY environment variables instead of the default app-config. The DB_PORT would still be sourced from app-config because the patch doesn’t override it.
This system solves the problem of managing configuration drift across environments. Instead of maintaining multiple Git branches with entirely different deployment files, you maintain a single template and use ConfigMaps (or Secrets) to provide environment-specific values. Flux, through its integration with Kustomize, handles the merging and substitution.
The core mechanism is Kustomize’s configMapGenerator and patchesStrategicMerge. The configMapGenerator creates ConfigMaps directly from literals or files. The patchesStrategicMerge allows you to overlay changes onto existing resources. When you use valueFrom.configMapKeyRef, the Kubernetes scheduler looks up the specified ConfigMap and key to populate the environment variable. Flux orchestrates this by applying the Kustomize transformation before applying the manifests to the cluster.
A common misconception is that ConfigMap values are directly embedded into the Deployment manifest. They are not. The Deployment manifest references the ConfigMap and its keys. Kubernetes then resolves these references at runtime when the Pod is created. This means if you update a ConfigMap, the Pods using it might need to be restarted to pick up the new values, depending on how the application is designed to handle configuration reloads. Flux will re-apply the ConfigMap and the Deployment if it detects changes in the Git repository, but it doesn’t automatically trigger Pod restarts for ConfigMap updates unless the Deployment’s spec.template.spec.containers[*].env section itself changes.
The most surprising thing about using ConfigMaps for variable substitution is how powerful and flexible it is, even without resorting to templating engines like Jinja or Helm for simple substitutions. Kustomize’s built-in generators and patch mechanisms are often sufficient. You can also generate ConfigMaps from files, allowing you to manage larger configuration sets externally.
The next concept you’ll likely encounter is managing sensitive data using Secrets in a similar fashion, and then exploring more advanced Flux features like ImagePolicy and ImageUpdateAutomation for continuous delivery.