Back to Helm & Charts
Q2Question 2 of 2

Helm Charts, Versioning, and Templating Releases Across Environments

Key Concepts
  • Helm is the package manager for Kubernetes, bundling templated manifests into versioned charts.
  • A chart's templates use Go templating to substitute values, so one chart renders different manifests per environment.
  • values.yaml holds defaults; environment-specific overlays like values-prod.yaml override them at deploy time.
  • Chart version tracks the package itself; appVersion tracks the application image it deploys.
  • helm install creates a release, helm upgrade records a new revision, and helm rollback reverts to a previous one.
  • The --atomic flag automatically rolls back a failed upgrade, which is essential for banking releases.

Definition

Helm packages Kubernetes manifests into versioned charts, parameterizes them with values files per environment, and manages install, upgrade, and rollback as tracked release revisions.

Helm is the package manager for Kubernetes.

  • Chart – a versioned bundle of templated Kubernetes manifests plus a default values.yaml.
  • Templates – use Go templating (e.g. {{ .Values.replicaCount }}) to substitute values, so one chart renders different manifests per environment.
  • Values – defaults live in values.yaml; environment overrides in values-dev.yaml, values-prod.yaml.
  • Versioning – version in Chart.yaml is the chart/package version; appVersion is the application image version.
  • Lifecycle – helm install creates a release, helm upgrade records a new revision, helm rollback reverts to a previous one.

At the National Bank of India, imagine the blueprint for setting up a new branch: the counters, the vault, the signage. Writing that blueprint by hand for every city is error-prone. Helm is a reusable branch blueprint where the city-specific details, like branch size and number of counters, are filled in from a small settings sheet. Dev gets a two-counter branch, production gets a fifty-counter branch, from the same blueprint. If a new branch setup goes wrong, Helm lets you revert to the previous working layout in one command.

Understanding the Concepts

What is a Helm chart?

A chart is a directory with a Chart.yaml describing its name and versions, a templates/ folder of manifest templates, and a values.yaml with defaults. Templates reference values with Go template expressions.

TEXT
transfer-chart/
  Chart.yaml          # name, version, appVersion
  values.yaml         # default values
  values-dev.yaml     # dev overrides
  values-prod.yaml    # production overrides
  templates/
    deployment.yaml   # templated Deployment
    service.yaml      # templated Service
    ingress.yaml      # templated Ingress
    _helpers.tpl      # named template helpers
YAML
# Chart.yaml
apiVersion: v2
name: transfer-service
version: 1.3.0        # chart/package version
appVersion: "2.1.0"   # application image version
description: Helm chart for the NBI Transfer Service

How does templating work?

Templates use Go templating to substitute values from the values file. Conditionals, loops, and named templates in _helpers.tpl are supported.

YAML
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-transfer
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-transfer
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-transfer
    spec:
      containers:
        - name: transfer
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.targetPort }}
          resources:
            requests:
              cpu: {{ .Values.resources.requests.cpu }}
              memory: {{ .Values.resources.requests.memory }}
            limits:
              cpu: {{ .Values.resources.limits.cpu }}
              memory: {{ .Values.resources.limits.memory }}
YAML
# values.yaml - defaults
replicaCount: 2
image:
  repository: nbi-registry.example.com/transfer-service
  tag: "2.1.0"
service:
  type: ClusterIP
  port: 80
  targetPort: 8080
resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    cpu: 500m
    memory: 512Mi

How do you parameterize per environment?

You keep base defaults in values.yaml and layer environment-specific overrides. At deploy time you select the right overlay with the -f flag.

YAML
# values-dev.yaml
replicaCount: 1
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 250m
    memory: 256Mi
YAML
# values-prod.yaml
replicaCount: 10
resources:
  requests:
    cpu: "1"
    memory: 1Gi
  limits:
    cpu: "2"
    memory: 1Gi
ingress:
  enabled: true
  host: api.nbi.com
BASH
# Deploy to dev
helm upgrade --install transfer-dev ./transfer-chart \
  -f values-dev.yaml -n dev

# Deploy to production
helm upgrade --install transfer-prod ./transfer-chart \
  -f values-prod.yaml -n prod

How does versioning work?

Versioning is two-dimensional. The chart version follows semantic versioning and changes when the chart's templates or defaults change. The appVersion tracks the application image. Keeping them distinct lets you ship a chart fix without bumping the app, or roll out a new app image without touching chart structure.

YAML
# Chart.yaml - chart fix adds a readiness probe, app unchanged
version: 1.4.0        # bumped: chart templates changed
appVersion: "2.1.0"   # unchanged: same application image

# Chart.yaml - new app release, chart unchanged
version: 1.4.0        # unchanged: no chart template changes
appVersion: "2.2.0"   # bumped: new application image
BASH
# Publish the chart to an OCI registry
helm package ./transfer-chart
helm push transfer-service-1.4.0.tgz oci://nbi-registry.example.com/charts

# Consume by version
helm install transfer oci://nbi-registry.example.com/charts/transfer-service \
  --version 1.4.0

How do install, upgrade, and rollback work?

The lifecycle commands map to tracked release revisions.

BASH
# Install creates release at revision 1
helm install transfer ./transfer-chart -f values-prod.yaml -n prod
# NAME: transfer
# STATUS: deployed
# REVISION: 1

# Upgrade applies changes and records revision 2
helm upgrade transfer ./transfer-chart -f values-prod.yaml -n prod
# Release "transfer" has been upgraded. Revision: 2

# History shows all revisions
helm history transfer -n prod
# REVISION  STATUS      CHART                   APP VERSION
# 1         superseded  transfer-service-1.3.0  2.1.0
# 2         deployed    transfer-service-1.4.0  2.2.0

# Rollback to revision 1
helm rollback transfer 1 -n prod
# Rollback was a success! Revision: 3 (copy of revision 1)

What is the --atomic flag?

The --atomic flag tells Helm to automatically roll back if an upgrade fails. This is essential for banking releases where a half-applied upgrade is worse than no upgrade.

BASH
# Production deploy with automatic rollback on failure
helm upgrade --install transfer-prod ./transfer-chart \
  -f values-prod.yaml \
  -n prod \
  --atomic \
  --timeout 5m

# If the new Pods fail readiness within 5 minutes,
# Helm automatically rolls back to the previous revision

What are common mistakes?

The most common mistake is not using --atomic for production, which can leave a half-broken release that requires manual intervention.

BASH
# WRONG - upgrade without --atomic
helm upgrade transfer-prod ./transfer-chart -f values-prod.yaml -n prod
# If the new version crashes, the release is stuck in "failed" state
# Manual rollback needed: helm rollback transfer-prod 1 -n prod

# CORRECT - always use --atomic for production
helm upgrade --install transfer-prod ./transfer-chart \
  -f values-prod.yaml -n prod --atomic --timeout 5m

Another mistake is relying on Helm rollback to undo database migrations run by Helm hooks. Helm rollback reverts Kubernetes objects but does not undo a migration hook's SQL.

How do you preview changes before applying?

Helm provides several safety tools for reviewing changes before applying them.

BASH
# Lint the chart for syntax and best-practice issues
helm lint ./transfer-chart -f values-prod.yaml

# Render templates locally without applying
helm template transfer ./transfer-chart -f values-prod.yaml

# Dry-run against the cluster (validates with API server)
helm upgrade --install transfer ./transfer-chart \
  -f values-prod.yaml --dry-run

# Show the diff between current and proposed (requires helm-diff plugin)
helm diff upgrade transfer ./transfer-chart -f values-prod.yaml

What should a senior engineer know?

Helm rollback, like a Deployment rollback, reverts the Kubernetes objects Helm manages but does not undo external side effects such as a database migration run by a Helm hook. Design migrations to be backward compatible so rollbacks remain safe.

Charts should be published to an OCI-compliant registry and consumed by pinned version so every deployment is reproducible and auditable.

For multi-team organizations, a chart library pattern lets teams share common templates (labels, security context, resource defaults) without copying YAML.

BASH
# Chart dependencies for shared libraries
# Chart.yaml
dependencies:
  - name: nbi-common
    version: 1.0.0
    repository: oci://nbi-registry.example.com/charts

Visual Overview

DIAGRAM
  Helm Chart Structure
  =====================

  transfer-chart/
  ├── Chart.yaml          version: 1.4.0, appVersion: 2.2.0
  ├── values.yaml         defaults (replicas: 2, cpu: 250m)
  ├── values-dev.yaml     dev overrides (replicas: 1)
  ├── values-prod.yaml    prod overrides (replicas: 10)
  └── templates/
      ├── deployment.yaml  {{ .Values.replicaCount }}
      ├── service.yaml     {{ .Values.service.port }}
      ├── ingress.yaml     {{ .Values.ingress.host }}
      └── _helpers.tpl     shared named templates


  Helm Release Lifecycle
  =======================

  helm install         helm upgrade         helm rollback
       │                    │                     │
       ▼                    ▼                     ▼
  ┌──────────┐        ┌──────────┐          ┌──────────┐
  │ Rev 1    │   →    │ Rev 2    │    →     │ Rev 3    │
  │ v1.3.0   │        │ v1.4.0   │          │ (copy of │
  │ app 2.1  │        │ app 2.2  │          │  Rev 1)  │
  │ deployed │        │ deployed │          │ deployed │
  └──────────┘        └──────────┘          └──────────┘
       ▲                                         │
       └─────────── rollback restores ────────────┘

Key Takeaway

Helm is the package manager for Kubernetes. A chart is a versioned bundle of templated manifests plus a default values file. Templates use Go templating to substitute values, so one chart can render a two-replica Deployment for dev or a ten-replica Deployment for production, purely by supplying a different values file. That eliminates configuration drift across environments.

Versioning is two-dimensional. The chart version tracks the package itself, and the appVersion tracks the application image. Keeping them separate means you can ship a chart fix without pretending the application changed, and vice versa. Charts published to a registry and consumed by pinned version make every deployment reproducible.

The lifecycle commands map to tracked revisions. Install creates a release at revision 1, upgrade records a new revision, and rollback reverts to a chosen revision. The --atomic flag is essential for production because it automatically rolls back a failed upgrade instead of leaving a half-broken release.

Remember one sentence: Helm templates one chart into many environments with values files, tracks every release as a numbered revision, and rolls back in one command.

---

Share
Use ← → arrow keys to navigate