← Back to portfolio

Building Helm Charts That Actually Work Across Environments

HelmKubernetesDevOpsDeployment

Helm charts are supposed to make Kubernetes deployments reproducible. In practice, most charts work in development and break in staging. Here is how to fix that.

The Values Hierarchy

A well-structured Helm chart uses four levels of values:

  1. Chart defaults (values.yaml): Sensible defaults that work for local development. Resource requests are low, replica count is 1, image pull policy is IfNotPresent.
  2. Environment overrides (values-staging.yaml, values-production.yaml): Resource requests, replica counts, and external service URLs specific to each environment.
  3. Secrets (external, never in Helm): Database credentials, API keys, TLS certificates. Managed by Kubernetes Secrets, referenced by name in the chart.
  4. GitOps overrides: Last-minute values set during deployment for things like image tags and feature flags.

The Template Patterns

Health checks first. Every deployment template should include liveness and readiness probes. Without them, Kubernetes cannot distinguish a starting container from a crashed one, and rolling updates become unpredictable.

Resource requests always, limits selectively. Memory limits prevent OOMKill propagation. CPU limits cause throttling. Set memory limits and omit CPU limits.

RBAC included. Every chart should create its own ServiceAccount, Role, and RoleBinding. Services should never run as the default service account.

The Testing

Running helm template in CI catches rendering errors before deployment. Adding helm lint and kubeval (validates generated YAML against the Kubernetes schema) catches the vast majority of chart bugs before they reach a cluster.