What Helm is and when it helps
Helm packages Kubernetes applications. A chart contains templated manifests, a values.yaml of defaults and a Chart.yaml describing the version. When you install it, Helm renders the templates, applies the result and records a release, which lets you list, upgrade and roll back the whole bundle.
That is genuinely useful in two situations: when you deploy the same application across several environments with small differences, and when you install software written by someone else. If you have one service in one cluster and a handful of YAML files, Helm may be more machinery than you need. Our Helm chart development work starts with that honest question, because the best chart is sometimes no chart. Teams looking for Helm consulting usually already have the symptoms: copy-pasted manifests, values nobody understands, or upgrades that fail halfway.
How we structure charts
One contract, few values
The values file is your chart’s public interface. Every option you add is something to document, test and support. We expose image, replicas, resources, ingress, environment variables and a small number of feature switches, and resist the urge to template every field.
A values schema is the cheapest protection against typos and wrong types:
# values.yaml, validated by a values.schema.json beside it
image:
repository: registry.example.com/orders-api
tag: "" # defaults to .Chart.AppVersion
replicaCount: 3
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { memory: 512Mi }
podDisruptionBudget:
minAvailable: 2
helm lint ./charts/orders-api
helm template orders ./charts/orders-api -f values/prod.yaml | kubeconform -strict -summary
helm upgrade --install orders ./charts/orders-api -n orders -f values/prod.yaml --atomic --timeout 5m
The --atomic flag rolls back a failed upgrade automatically, which is a sensible default for pipelines that deploy directly. Under GitOps the controller handles this instead.
Library charts and umbrella charts
If thirty services each carry their own deployment template, fixing a probe means thirty edits. A library chart holds shared templates, and service charts depend on it. Umbrella charts, which bundle several sub-charts into one release, are tempting but couple unrelated lifecycles together. We use them sparingly, mostly for tightly bound components.
Security and access
Charts run with whatever permissions the installer has, so access matters. We restrict who can deploy to which namespace with Kubernetes RBAC, and we review third-party charts before adopting them: what they create, which cluster roles they request, and whether they run privileged. Pin chart versions and, where supported, verify provenance or signatures.
Secrets need a deliberate pattern. Values files in Git must not carry plaintext credentials; see Sealed Secrets and External Secrets Operator. Charts should set a restrictive pod security context by default, so teams have to opt out rather than opt in.
Cost and capacity
Helm does not cost anything to run, but charts decide what your workloads cost. Missing resource requests mean poor bin-packing, and oversized defaults waste nodes. We bake in realistic requests, a limit policy and a disruption budget, then tune from real usage. A shared chart makes that one change rather than thirty, which is where the practical savings come from.
Day-2 operations
Upgrades
Chart upgrades are where teams get hurt. Review the diff before applying: the helm diff plugin or your GitOps tool’s diff view shows exactly what will change. Read upstream release notes for breaking changes to values. CRDs are not upgraded by Helm from a chart’s crds directory, so manage them explicitly. Immutable fields, such as a StatefulSet’s selector, cannot be patched and need a planned recreate.
Repositories and promotion
We publish charts to an OCI registry or a repository, version them with semantic versioning, and promote through environments by pull request. Details are in Helm chart repository setup. Delivery with ArgoCD or Flux is covered in migrating from kubectl and Helm to GitOps.
Helm and Kustomize compared
| Need | Helm | Kustomize |
|---|---|---|
| Install third-party software | Natural fit, most vendors publish charts | Possible, but you vendor and patch YAML |
| Per-environment differences | Values files | Overlays and patches |
| Versioned, shareable package | Yes, via registry or repository | No |
| Release history and rollback | Built in | Left to your GitOps tool |
| Learning curve | Go templates and values design | Lower, plain YAML |
Neither is universally better. We often use Helm for the application and a thin Kustomize layer for cluster-specific tweaks, and our guide to multi-environment promotion with Kustomize shows where that boundary works well.
Common failure modes
- Hooks that hang or leave state, such as migration jobs with no clean-up policy.
- Values drift, where
helm upgrade --reuse-valueshides what is really deployed. Prefer explicit values files in Git. - Stuck releases in a pending state after an interrupted upgrade, which need careful recovery rather than deletion.
- Template sprawl: nested conditionals and
tplcalls that nobody can read. Simplify or split the chart. - Unpinned dependencies that change under you between builds.
When not to use Helm, and migration paths
If your team only patches environments of manifests you own, Kustomize is simpler and needs no release concept. For software that manages itself, an operator may be a better answer than a chart. If your templating logic keeps growing, consider a typed configuration language. Moving away from Helm is usually straightforward: render with helm template and take the output as your starting manifests. For clusters running on Kubernetes, we can review your charts in a short audit. Book a consultation to discuss yours.
Official documentation: helm.sh/docs