Helm Charts for Kubernetes: Packaging, Templating, Versioning, and Release Strategies
Helm Charts for Kubernetes covers packaging, templates, versions, and releases. Learn what breaks on upgrade, how to pin charts, and how to roll back.
Helm Charts for Kubernetes package the objects that run a service so you can install and upgrade them as one release. When the chart is a pile of copies, upgrades drift and rollbacks lie. Therefore you should treat the chart as a versioned product with a small values API.
What Helm Is and Why It Fails in Production
Helm renders templates with a values file, then applies the result as a release. It stores release history so you can roll back to a prior render. If you edit live objects by hand, the next upgrade will fight you.
In my experience, Helm fails in production for a few plain reasons. First, the chart that ran in staging is not the chart that ran in prod, because values were edited in the cluster. Then, a template change renames a resource, and the upgrade deletes the old one. Also, the image tag floats, so a rollback restores an old tag that now points at new bytes.
A common mistake I have seen is a values file that grew into a second programming language. Every team forked a bool. Because the defaults changed upstream, a quiet upgrade turned a sidecar on in prod.
After the incident, nobody could say which value was the contract. The chart had no stable API.
Another failure is CRD order. The chart installs a custom resource before the definition exists, or it upgrades the definition in a way old controllers cannot read. When the release shows deployed, the pods are still crash looping.
Still, Helm exits zero. So check pod health after the release, not only the Helm status.
You also fail when secrets sit in values committed to git. Anyone with the repo can read them, and they land in release history too. Pass secrets from a store at deploy time, or use an external secret object. Do not bake the password into the chart.
Architecture and Implementation
A chart is a set of templates, a values schema, and a version. First, keep templates dumb. Next, keep values small. Then, pin the chart version and the image digest in the deploy repo.
Finally, roll out with the same health gates you trust for any other deploy. The Helm charts docs define the layout. Follow it so tools can package your chart.
Templates and values
Templates should stamp out Kubernetes objects, not hide a novel language. The Helm template guide shows the syntax. If a reviewer cannot tell what object will exist, the template is too clever.
Expose only values a caller must set. Image, size, and env name are enough for many services. If you expose every nested field, callers will depend on your private shape. Then you cannot fix the chart without a breaking change.
Give defaults that are safe. A default of a public service or an empty password is a bug. When a value is required, fail the render if it is missing. Do not deploy a half config that happens to render.
Versions and dependencies
Chart version is the package. App version is the software inside. Bump the chart version for every template or default change you publish. If you reuse a version, a cache can serve the old tarball and prod will not match the git you think you deployed.
Pin dependency charts. A floating dependency will change your render on a random day. When you bump it, read the notes and diff the rendered YAML. Your Docker images should be pinned by digest inside values for prod, not by a moving tag.
Releases and rollbacks
A release name plus a namespace identifies the install. Keep one release per service per environment. If two tools manage the same objects, they will overwrite each other.
The Helm upgrade docs cover the command. Use upgrade with install so the first deploy and the next deploy share a path.
Rollback restores the previous release’s rendered objects. It does not undo a database migration or a manual edit outside the chart. Your rollback strategies should say that Helm rollback is only the Kubernetes half. Test it before you trust it at 2 a.m.
Hook weights and hook deletes cause surprises. A pre upgrade hook that fails can leave the release pending. A hook job that is not cleaned up fills the namespace.
Use hooks for rare jobs. Prefer a normal Job or an init container when the work is part of the app.
How to Ship a Chart
Render in CI and diff the YAML on the pull request. Humans review objects better than they review template branches. Your CI/CD pipelines should fail if the render is invalid or if a required value is empty.
Store charts in a registry you control. Deploy by chart version plus a values file that is also versioned. When prod and staging differ, differ in values, not in forked templates. A fork will miss the next security fix.
- Keep the values API small and documented.
- Bump the chart version on every publish.
- Pin image digests and dependency chart versions.
- Diff the rendered YAML in review.
- Upgrade with a health wait, then confirm pods.
Trade-offs and Comparison
Helm is useful when you repeat the same service shape many times. It is heavy when you have one Deployment and a ConfigMap. Plain YAML or a lighter tool can be clearer there. Pick the tool the on call engineer can debug.
Umbrella charts that install the whole company look convenient. They create one giant release, one giant blast radius, and a rollback that reverts unrelated services. Prefer one chart per service. Compose them in the pipeline, not in one parent chart.
| Approach. | When to use it. | What you give up. |
|---|---|---|
| Plain manifests. | One or two simple objects. | You write more boilerplate. |
| Helm chart. | A repeated service shape and a values API. | Template debug cost and release history. |
| Umbrella chart. | A demo that installs many parts once. | Huge blast radius on upgrade. |
| Live kubectl edits. | Almost never in prod. | The next upgrade will wipe them. |
Choose a chart when at least two environments share the shape. If the template logic needs a story to explain, stop and split the chart. More if blocks are not flexibility. They are future incidents.
Pitfalls and Failure Modes
Resource renames delete and recreate. A Deployment name change during upgrade drops pods and can drop a volume you meant to keep. If you must rename, do it as a planned migration, not as a cleanup commit. Read the diff for delete lines.
Three way merge surprises people. Helm tries to respect fields it does not own. Sometimes that keeps a bad live edit alive.
Sometimes an upgrade still stomps a hot fix. After an incident edit, put the fix back into values and upgrade. Do not leave the cluster as the only copy.
Pending or failed releases block the next upgrade. A killed CI job can leave the release in a stuck state. You then need a careful rollback or uninstall of the pending secret.
Check helm history before you retry in a loop. Two retries can make the history worse.
Values and Kubernetes scheduling limits drift apart. The chart default requests are tiny. Prod sets nothing, so the default wins, and the service throttles.
Required values for requests beat silent defaults. Make the caller set size on purpose.
Subcharts do not share values unless you wire them. A parent sets image.tag and the child still uses its own default. Then you run mixed versions and call it one release.
Render and grep the image fields. Do not trust the parent values file alone.
Rollouts that ignore readiness look green in Helm and red for users. Wait for the Deployment. Your rolling deployments still need max unavailable and a ready probe.
Helm will not invent those for you. If the wait times out, treat it as a failed release and decide on rollback with the error in hand.
A Practical Values File
The sketch below is a small values file for one service. It sets a replica count, an image, and requests. When you deploy, pass the prod file on purpose. Do not rely on a default environment name of prod.
name: api
environment: staging
replicaCount: 3
image:
repository: registry.example.com/api
digest: sha256:abc123
service:
port: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
Note what this sketch does not do. It does not embed a password. It does not use a latest tag.
Pair it with a template that fails when the digest is empty. Also diff the render in CI so a template change cannot sneak a new object into prod without review.
Performance, Scale, and Cost
Helm itself is rarely the CPU bill. The cost is bad upgrades, huge manifests, and release secrets that bloat etcd. A chart that renders thousands of objects will make the API server sweat.
Split it. Keep release history trimmed so old secrets do not pile up.
Render time grows with template tricks and with subcharts that fetch remote indexes. Pin a local chart cache in CI. A network fetch on every deploy makes the path flaky and slow. If the chart repo is down, you should still be able to redeploy the last known chart.
At scale, values sprawl is the human cost. Fifty files override the same key in different ways. Nobody knows the effective config.
Render the effective values in the pipeline log. Make one prod file the contract, and ban snowflake edits.
We once hit a bottleneck when an umbrella chart upgraded twenty services as one release. A bad template in one service rolled back the whole platform, including healthy parts. The fix was one chart per service and a pipeline that upgrades them in order. After that, a failure stayed inside one release history.
Chart size also slows review. A render diff of ten thousand lines will not be read. If the diff is that big, the change is too big.
Split the pull request. Keep routine image bumps to a digest change so the diff stays obvious.
Plan capacity for rollback, not only for upgrade. A rollback pulls the previous image and must fit on the node. If you deleted that image from the registry to save money, rollback will fail when you need it.
Keep the last few digests. The storage cost is cheap next to an outage.
Key Takeaways
- Keep a small values API and safe defaults.
- Bump the chart version every time you publish.
- Pin image digests and dependency charts.
- Review rendered YAML, not only the template diff.
- Use one release per service so rollback stays local.
- Treat Helm rollback as the Kubernetes half, not the data half.
- Put live hot fixes back into git before the next upgrade.
FAQ
Should I commit rendered YAML?
Commit the chart and the values. You can also commit the render if you want review without a Helm install. Either way, the thing you apply should match a reviewed render. Do not apply a local render that never saw CI.
How many old releases should I keep?
Keep enough to roll back past the last bad upgrade, often a handful, an illustrative production range. More history means more secrets in the cluster. Trim on purpose. If you keep one, you cannot roll back past the current bug.
Are Helm hooks a good place for migrations?
Only if the migration is safe to retry and safe with the old and new app. A hook that fails mid way can stick the release. Many teams run migrations as a separate pipeline step with a clearer lock. Pick one place so two tools do not migrate at once.
What is the difference between chart version and app version?
Chart version tracks the package and templates. App version tracks the software you ship. Bump chart version for a template fix even when the app bits did not change. If you skip that bump, clients will keep the cached chart and miss the fix.
Helm Charts for Kubernetes stay manageable when the values API is small and the release is one service. Pick one chart you own. Next, pin the image digest, bump the chart version, and diff the render in CI. Then practice a rollback in staging so you know what it does and what it leaves behind.
Last updated on 22 September 2026.