Helm lets you override default chart values using multiple -f flags, but the order matters, and it’s not always obvious how they interact.

Imagine you’re deploying a web application with Helm. The default chart has a replicaCount of 1. You want 3 replicas, so you create a my-values.yaml file:

replicaCount: 3

And run:

helm install my-release ./my-chart -f my-values.yaml

This works. Now, let’s say you have a production environment where you need even more replicas, say 5. You create prod-values.yaml:

replicaCount: 5

And try:

helm install my-release ./my-chart -f my-values.yaml -f prod-values.yaml

Which one wins? The last one specified on the command line. Helm merges these files sequentially, with later files overriding earlier ones. So, replicaCount will be 5.

What if you also want to set a specific resource limit, but only for a staging environment, and you want that to be overridden by your production settings?

staging-values.yaml:

resources:
  limits:
    cpu: 500m
    memory: 512Mi

prod-values.yaml:

replicaCount: 5
resources:
  limits:
    cpu: 1000m
    memory: 1024Mi

Running helm install my-release ./my-chart -f my-values.yaml -f staging-values.yaml -f prod-values.yaml will result in:

  • replicaCount: 5 (from prod-values.yaml, overriding my-values.yaml)
  • resources.limits.cpu: 1000m (from prod-values.yaml, overriding staging-values.yaml)
  • resources.limits.memory: 1024Mi (from prod-values.yaml, overriding staging-values.yaml)

This sequential override behavior is key. Helm doesn’t do a deep merge where it combines all values; it’s a last-write-wins approach for any given path in the YAML structure.

Consider a more complex scenario where you have a base configuration, environment-specific overrides, and then a final, truly custom override for a specific deployment.

base-values.yaml:

image:
  repository: nginx
  tag: latest
service:
  type: ClusterIP
  port: 80
replicaCount: 1

dev-values.yaml:

replicaCount: 2
image:
  tag: dev
service:
  port: 8080

prod-values.yaml:

replicaCount: 5
resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: 800m
    memory: 800Mi

If you run helm install my-release ./my-chart -f base-values.yaml -f dev-values.yaml -f prod-values.yaml, the final values will be:

  • image.repository: nginx (from base-values.yaml, not overridden)
  • image.tag: dev (from dev-values.yaml, overrides base-values.yaml)
  • service.type: ClusterIP (from base-values.yaml, not overridden)
  • service.port: 8080 (from dev-values.yaml, overrides base-values.yaml)
  • replicaCount: 5 (from prod-values.yaml, overrides base-values.yaml and dev-values.yaml)
  • resources.requests.cpu: 200m (from prod-values.yaml)
  • resources.requests.memory: 256Mi (from prod-values.yaml)
  • resources.limits.cpu: 800m (from prod-values.yaml)
  • resources.limits.memory: 800Mi (from prod-values.yaml)

Notice how dev-values.yaml’s replicaCount of 2 is completely lost because prod-values.yaml specifies it again. This is the core behavior to grasp: each subsequent -f file merges its contents into the combined values from previous files, with its own values taking precedence.

The order of -f flags is crucial for managing configuration across different environments or for applying specific, targeted overrides. You can also use the --set flag, which is applied after all -f files, giving it the highest precedence.

This sequential merging is also how Helm resolves values when using --reuse-values or when upgrading. The existing values are treated as the "base" and new values are merged on top in the order they appear.

The most surprising thing about this mechanism is that it doesn’t perform any intelligent merging for lists. If a list is defined in an earlier file and then redefined in a later file, the entire list from the later file replaces the list from the earlier file, rather than appending or merging elements. For instance, if you have tolerations in two files, the one from the last file specified will be the only one applied, not a combined list.

Understanding this last-write-wins strategy for all value types, including nested maps and lists, is fundamental to mastering Helm value overrides.

The next concept you’ll likely grapple with is how to manage secrets effectively using Helm.

Want structured learning?

Take the full Helm course →