Helm Values and Overrides#
Every Helm chart has a values.yaml file that defines defaults. When you install or upgrade a release, you override those defaults through values files (-f) and inline flags (--set). Getting the precedence wrong leads to silent misconfigurations where you think you set something but the chart used a different value.
Inspecting Chart Defaults#
Before overriding anything, look at what the chart provides. helm show values dumps the full default values.yaml for any chart:
# From a repo
helm show values bitnami/postgresql
# From a local chart directory
helm show values ./my-chart
# From an OCI registry
helm show values oci://registry-1.docker.io/bitnamicharts/postgresql
# Pipe to a file for reference
helm show values bitnami/postgresql > postgresql-defaults.yamlThis is the single most important debugging command. When something behaves unexpectedly, compare your overrides against these defaults.
Override Precedence#
Helm merges values in a specific order. Later sources override earlier ones:
- Chart’s
values.yaml(lowest priority) - Parent chart’s
values.yaml(if this is a subchart) - Values files passed with
-f/--values(left to right) - Inline values with
--set,--set-string,--set-file,--set-json(highest priority)
The critical rule: rightmost wins. If you pass multiple -f flags, the last file takes precedence for any conflicting keys:
# base.yaml sets replicas: 1
# production.yaml sets replicas: 3
helm upgrade --install my-app ./chart \
-f values/base.yaml \
-f values/production.yaml
# Result: replicas = 3 (production.yaml wins)And --set always beats -f, regardless of ordering:
helm upgrade --install my-app ./chart \
--set replicaCount=5 \
-f values/production.yaml
# Result: replicas = 5 (--set wins over -f, always)–set vs -f: When to Use Which#
Use -f for structured, version-controlled configuration. Use --set for one-off overrides, CI/CD pipeline variables, and secrets you do not want in files.
# -f for environment config (committed to git)
helm upgrade --install my-app ./chart -f values/base.yaml -f values/staging.yaml
# --set for dynamic values from CI/CD
helm upgrade --install my-app ./chart -f values/production.yaml --set image.tag="${GIT_SHA}"
# --set-string forces string type (important for numeric-looking values)
helm upgrade --install my-app ./chart --set-string podAnnotations."prometheus\.io/port"="9090"
# --set-json for complex structures inline
helm upgrade --install my-app ./chart --set-json 'resources={"limits":{"cpu":"500m","memory":"256Mi"}}'Watch the escaping. Dots navigate nested keys (ingress.hosts[0].host=example.com). Escape literal dots with backslash (nodeSelector."kubernetes\.io/arch"=arm64). Avoid comma-separated --set a=1,b=2 – use separate --set flags instead.
Dry-Run and Template Rendering#
Before applying changes, render templates locally to see the actual manifests Helm will produce:
# helm template: renders locally, no cluster connection needed
helm template my-release ./chart -f values/production.yaml
# helm template with a specific template file
helm template my-release ./chart -s templates/deployment.yaml
# helm upgrade --dry-run: renders against the cluster (validates API versions, checks existing resources)
helm upgrade --install my-app ./chart \
-f values/production.yaml \
--dry-runThe difference matters. helm template works offline but cannot validate against your cluster’s API capabilities. --dry-run contacts the cluster, so it catches issues like missing CRDs or unsupported API versions.
Pipe the output to search for specific values to confirm your overrides took effect:
helm template my-release ./chart -f values/production.yaml | grep -A5 "resources:"Values Schema Validation#
Charts can include a values.schema.json file that validates values before rendering. If the schema exists, Helm rejects invalid values at install/upgrade time:
# This will fail if replicaCount must be an integer and you pass a string
helm upgrade --install my-app ./chart --set replicaCount=abc
# Error: values don't meet the specifications of the schemaWhen writing your own charts, add a values.schema.json to catch misconfigurations early. Even a minimal schema that enforces required fields and types saves debugging time.
Environment-Specific Values Pattern#
The most common pattern is a layered file structure with base defaults and per-environment overrides:
my-chart/
values/
base.yaml # shared defaults
dev.yaml # dev overrides (low resources, debug logging)
staging.yaml # staging (moderate resources, staging URLs)
production.yaml # production (high resources, real URLs, replicas)# Dev deployment
helm upgrade --install my-app ./chart -f values/base.yaml -f values/dev.yaml
# Production deployment
helm upgrade --install my-app ./chart -f values/base.yaml -f values/production.yamlKeep base.yaml as the source of truth for structure, and environment files as minimal diffs. Do not duplicate the entire values file per environment – only override what changes.
Helm OCI Registry Support#
Helm 3.8+ supports OCI registries as chart repositories. This replaces the older helm repo add workflow:
# Pull a chart from an OCI registry
helm pull oci://registry-1.docker.io/bitnamicharts/postgresql --version 16.4.1
# Install directly from OCI
helm upgrade --install my-pg oci://registry-1.docker.io/bitnamicharts/postgresql \
--version 16.4.1 \
-f values/postgresql.yaml
# Push your own chart to an OCI registry
helm package ./my-chart
helm push my-chart-1.0.0.tgz oci://ghcr.io/myorg/charts
# Login to a private OCI registry
helm registry login ghcr.io -u USERNAME -p TOKENOCI charts do not need helm repo update. Each pull fetches the exact version. This makes builds more reproducible and eliminates stale repo index issues.
Debugging Values Problems#
When a deployment does not behave as expected:
# See what values were explicitly set for a release
helm get values my-release -n my-namespace
# See everything, including chart defaults
helm get values my-release -n my-namespace --all
# See the rendered manifests that were applied
helm get manifest my-release -n my-namespace
# Diff between two revisions (requires helm-diff plugin)
helm diff revision my-release 3 4Compare the output of helm get values --all against helm show values <chart> to spot unintended defaults overriding your configuration.