Alright, let’s talk about Helm values. This is where Helm stops being a cute little package installer and starts to feel like the powerful, slightly terrifying configuration management tool it actually is. Think of the default values.yaml file that comes with a chart not as a complete settings menu, but as a suggestion. A very strong, “we-probably-thought-about-this-more-than-you-did” suggestion, but a suggestion nonetheless. Your job is to know how to override these suggestions without causing a multi-pod pileup.

The Hierarchy of Override Authority

Helm doesn’t just let you yell configuration into the void and hope it listens. It follows a strict, predictable hierarchy for applying values. From least powerful to most powerful, it goes:

  1. The chart’s values.yaml (The defaults, the original suggestion).
  2. A user-supplied values file (via -f or --values).
  3. Multiple --set arguments on the command line.
  4. The --set-file argument for loading values from a file.

The key thing to remember is that anything specified later in this chain will override anything specified earlier. This is your primary tool for customizing a deployment without forking the chart’s code. It’s the difference between “I’ll take the burger as-is” and “I’ll take the burger, but hold the pickles, add bacon, and cook it medium-well.”

The Art of the --set Argument

The --set flag is your quick-and-dirty, command-line fix. It’s perfect for when you need to change one or two things for a quick test. The syntax, however, can feel a bit like you’re trying to whisper a secret password to a grumpy bouncer.

Need to change the number of replicas? Easy.

helm install my-release bitnami/nginx --set replicaCount=2

But what about nested values? This is where it gets fun. You use a dotted notation.

# Inside the values.yaml, this might look like:
# service:
#   type: ClusterIP
#   port: 80

To override that, you’d use:

helm install my-release bitnami/nginx --set service.type=LoadBalancer --set service.port=8080

And for arrays? Brace yourself. Literally. You use curly braces {} to denote list elements.

# Let's say the chart expects:
# tolerations:
#   - key: "key1"
#     operator: "Equal"
#     value: "value1"
#     effect: "NoSchedule"

To set this via --set, you have to write a small JSON-like object in your terminal:

helm install my-release my-chart --set tolerations[0].key="key1" --set tolerations[0].operator="Equal" --set tolerations[0].value="value1" --set tolerations[0].effect="NoSchedule"

See what I mean? It’s absurd. It works, but for anything more complex than a simple value, you’re going to want to…

Use a Values File, You Maniac

For any serious deployment, --set is a trap. It’s error-prone, hard to version control, and a nightmare to audit. The professional move is to use your own values.yaml file. This is where you explicitly declare your overrides.

Create a file, let’s call it my-production-values.yaml:

# my-production-values.yaml
replicaCount: 3
service:
  type: LoadBalancer
  port: 443
image:
  tag: "1.19.10-debian-10-r0" # Pin your tags, folks. Never use 'latest' in prod.
resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

Then install it with supreme confidence:

helm install my-release bitnami/nginx -f ./my-production-values.yaml

The beauty here is that Helm performs a deep merge. Your file doesn’t need to contain the entire default values.yaml. It only needs the parts you want to change. Helm will intelligently layer your overrides on top of the chart’s defaults. This file is now a first-class citizen in your infrastructure code—you can check it into git, review it, and know exactly what configuration produced which release.

The Nuclear Option: --set and -f Together

Sometimes you need to combine approaches. Maybe you have a solid base values file for production, but you need to do a one-off debug deployment with more verbose logging. This is where you combine -f and --set.

helm install my-debug-release my-chart -f ./production-values.yaml --set logLevel=debug

In this case, the logLevel=debug set on the command line will override even the logLevel value that might be present in your production-values.yaml file. It’s the highest authority. Use this power wisely; it’s best for temporary, situational overrides, not for permanent configuration.

The most common pitfall? Typos. Helm won’t yell at you if you set servise.port=443 instead of service.port. It will just silently ignore your typo and use the default, leaving you to wonder why your service is still on port 80. Always, always use helm template [your-chart] -f your-values.yaml to render the templates locally and see exactly what Kubernetes manifests Helm is generating before you actually install anything. It’s the equivalent of measuring twice and cutting once. Trust me, your future self, who isn’t debugging a misconfigured pod, will thank you.