Skip to content
C Codeloom
Kubernetes

Introduction to Kubernetes Helm Charts

Learn what Helm charts are, how templates and values work together, and how to package your own application for repeatable, parameterized Kubernetes deployments.

·4 min read · By Codeloom
Intermediate 9 min read

What you'll learn

  • What problem Helm solves
  • Chart structure and templating syntax
  • Values, overrides, and environments
  • Releases, rollbacks, and upgrades
  • When Helm is the wrong tool

Prerequisites

  • Basic familiarity with Kubernetes manifests

What and Why

Hand-writing a separate set of YAML manifests for dev, staging, and production gets old fast. You end up with subtle drift, copy-paste mistakes, and brittle sed pipelines in your CI. Helm is Kubernetes’ package manager. A chart is a parameterized bundle of manifests; a release is an instance of that chart installed in a cluster.

Helm turns “10 YAML files copy-pasted three times” into “one chart, three values files.”

Mental Model

A chart is a directory with templates and a default values file. At install time, Helm renders the templates by substituting values, then sends the result to the Kubernetes API. The state of each release is stored as a Secret in the target namespace, which is how Helm supports upgrades and rollbacks.

  Chart/                 values.yaml  -overrides->  values-prod.yaml
 templates/                  \        /
   deployment.yaml            \      /
   service.yaml                v    v
   ingress.yaml             [helm template]
                                  |
                                  v
                        [rendered manifests]
                                  |
                                  v
                        kubectl apply (via Helm)
                                  |
                                  v
                      [release stored as Secret]
Helm render and install flow

Hands-on Example

Scaffold a chart:

helm create myapp

You get this layout:

myapp/
  Chart.yaml
  values.yaml
  templates/
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl

Trimmed values.yaml:

replicaCount: 2
image:
  repository: myorg/myapp
  tag: "1.0"
service:
  type: ClusterIP
  port: 80
ingress:
  enabled: false
  host: app.example.com

templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels: {{- include "myapp.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels: {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels: {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: app
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: 80

Install once, override per environment:

helm install myapp ./myapp -f values-prod.yaml \
  --set image.tag=1.4.2 --namespace prod

Upgrade later:

helm upgrade myapp ./myapp -f values-prod.yaml \
  --set image.tag=1.4.3 --namespace prod

Roll back if something breaks:

helm history myapp -n prod
helm rollback myapp 4 -n prod

Common Pitfalls

Treating templates like code. Go templating gets ugly fast with conditionals nested inside conditionals. If your chart has more {{ if }} than YAML, consider Kustomize or a thin code-based tool (Pulumi, cdk8s) instead.

Embedding secrets in values.yaml. Never commit production secrets. Use --set from CI, Helm secrets plugins, or an external secret operator.

Ignoring chart versioning. Chart.yaml has version (chart) and appVersion (app). Bump the chart version on every change; consumers rely on it for rollback.

Forgetting helm lint. Run it in CI. It catches templating errors before they hit a cluster.

CRD upgrades. Helm installs CRDs once and never updates them by default. Charts with CRDs need a deliberate upgrade procedure.

--force on upgrade. It deletes and recreates resources, causing downtime. Almost never the right answer.

Practical Tips

Use helm template to render locally without touching a cluster:

helm template myapp ./myapp -f values-prod.yaml > rendered.yaml

Inspect rendered.yaml before any production rollout, especially when changing template logic.

Pull from public repos to avoid reinventing wheels:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm install postgres bitnami/postgresql -f my-postgres-values.yaml

For multi-environment workflows, structure values files like:

charts/myapp/
  values.yaml          # defaults
  values-dev.yaml
  values-staging.yaml
  values-prod.yaml

Each environment file overrides only what differs. Keep defaults safe.

Combine with GitOps tools like Argo CD or Flux. Both natively understand Helm and let you commit values files to Git while the controller reconciles releases continuously.

For complex apps, embrace library charts for reusable templates and subcharts for shared dependencies. But fight the temptation to build a generic chart that handles every app in your org — those become unmaintainable.

Wrap-up

Helm makes Kubernetes deployments repeatable, parameterized, and rollback-friendly. Start by scaffolding a chart, keep templates simple, separate environments with values files, and use helm template and helm lint as part of your CI. When templating gets out of hand, reach for Kustomize overlays or a typed alternative. Used with discipline, Helm is the lowest-friction way to ship the same app to dev, staging, and production without the YAML copy-paste tax.