One Chart, Many Environments: Values Files for Dev, Staging, and Prod
Override just what differs between environments, and understand the order values are merged in.
A chart’s values.yaml holds sensible defaults, but dev, staging, and production rarely share a namespace, a replica count, or a config value. This guide covers layering additional values files per environment, without maintaining separate copies of the chart itself. It builds on the webapp chart from the previous guide.
Keep values.yaml as the shared baseline
Leave anything that should be identical everywhere, the app name, the image repository, in values.yaml. Only environment-specific fields belong in an override file:
# values-dev.yaml
namespace: dev
config:
customHeader: "welcome to dev"
# values-prod.yaml
namespace: prod
replicaCount: 5
config:
customHeader: "welcome to production"
A file passed with -f doesn’t replace values.yaml, it’s merged on top of it. values-dev.yaml only needs to mention namespace and config.customHeader; everything else, appName, image, falls through from the base file unchanged.
Reference the namespace in your templates
Add the value and use it wherever a manifest needs to declare its namespace:
# values.yaml (add a default)
namespace: default
# templates/deployment.yaml, templates/service.yaml, templates/configmap.yaml
metadata:
name: {{ .Values.appName }}
namespace: {{ .Values.namespace }}
Install with an environment’s values file
kubectl create namespace dev
kubectl create namespace prod
helm install web-dev ./webapp -f values-dev.yaml -n dev
helm install web-prod ./webapp -f values-prod.yaml -n prod
Two things are doing related but distinct jobs here: -n dev tells Helm which namespace to store the release’s own metadata in, and metadata.namespace inside the templates tells Kubernetes which namespace each resource goes into. Keeping them the same, the release lives in the same namespace as what it deployed, is standard practice and avoids confusion later when you run helm list -n dev and expect to see everything that’s actually running there.
helm list -A
kubectl get all -n dev
kubectl get all -n prod
curl <dev-pod-ip> # or kubectl port-forward, per NOTES.txt
Confirm each environment shows its own customHeader and replica count.
The full precedence order
Helm merges values from several sources, each overriding the ones before it:
values.yaml, the chart’s own defaults.- A parent chart’s
values.yaml, if this chart is used as a dependency (not covered here). - Any file passed with
-f, in the order given, later files win over earlier ones. --set,--set-string, or--set-jsonflags, which win over every file.
helm upgrade web-dev ./webapp -f values-dev.yaml --set replicaCount=1
--set is convenient for a one-off override or a quick test, but for anything you want to reproduce, put it in a file and check it into version control. A --set flag typed at a terminal leaves no record of what was actually deployed.
Two ways to check what a merge produced
Before trusting an install or upgrade, look at the result:
helm template ./webapp -f values-prod.yaml
helm get values web-prod
helm template -f renders the chart with a given values file exactly as install would, without touching the cluster, so you can diff two environments’ output directly. helm get values (after install) shows the values actually recorded against a live release, which is what to check when a running release doesn’t match what you expect a file to produce.
A note on secrets
Values files are plain text, so don’t put real credentials in one committed to Git. A common split is: non-sensitive per-environment config in values-<env>.yaml, and secrets injected either via --set from a CI secret store, via a tool like Sealed Secrets or SOPS, or by having the chart reference a Kubernetes Secret created outside of Helm rather than embedding the value in values.yaml at all.
Popular Guides
-
Helm Chart Anatomy: Charts, Values, Templates, and Releases The folder structure every chart shares, and the three concepts that make Helm more than a YAML bundler. -
Create Your First Helm Chart from Existing Manifests Turn a Deployment, Service, and ConfigMap into a templated chart, then install and upgrade it. -
Manage Releases: Install, Upgrade, Rollback, and Uninstall The commands that cover a release's whole lifecycle, and how revision history makes rollback possible.