Skip to content
C Codeloom
Kubernetes

Helm Basics: Kubernetes Package Manager

Learn Helm fundamentals: helm install, charts, values.yaml, templating, releases, and when to choose Helm over plain Kubernetes YAML manifests.

·6 min read · By Yash Kesharwani
Intermediate 11 min read

What you'll learn

  • What Helm is and the problem it solves
  • How to install, upgrade, and roll back releases
  • The structure of a chart and the role of values.yaml
  • How Go templating renders manifests with values and helpers
  • When Helm is the right tool and when raw YAML is better

Prerequisites

  • A working Kubernetes cluster and kubectl
  • Familiarity with [pods, deployments, and services](/blog/kubernetes-pods-deployments-services)
  • Comfort with [configmaps and secrets](/blog/kubernetes-configmaps-and-secrets)

Once you have written a few Kubernetes deployments, services, and configmaps by hand, the next problem appears: how do you package them so they can be installed by someone else, parameterized for different environments, and rolled back cleanly? Helm is the most widely used answer. It is the de facto package manager for Kubernetes.

This article walks through what a Helm chart is, how the templating system works, and how you operate on releases — plus an honest take on when Helm helps and when plain YAML is enough.

Installing Helm

Helm is a single client-side binary that talks to your cluster through the existing kubeconfig. On macOS or Linux:

brew install helm
# or
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

helm version

There is no server-side component. Helm 3 stores release state in Kubernetes Secrets inside the namespace where the release lives.

Installing a chart from a repository

The fastest way to see Helm in action is to install a public chart:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install my-redis bitnami/redis

What just happened:

  1. Helm pulled the redis chart from the bitnami repo.
  2. It rendered the chart’s templates into final Kubernetes manifests using default values.
  3. It applied those manifests to the current namespace.
  4. It stored a record of this release named my-redis.

You can inspect everything that got created:

helm list
helm status my-redis
kubectl get all -l app.kubernetes.io/instance=my-redis

Releases, upgrades, and rollbacks

A release is a specific installation of a chart. The same chart can be installed many times under different release names, in different namespaces, with different values. Each install or upgrade creates a new revision.

helm upgrade my-redis bitnami/redis --set auth.password=newsecret
helm history my-redis
helm rollback my-redis 1

helm rollback reverts to a previous revision in seconds. This is one of the underrated wins of Helm: a single command undoes a complex multi-resource change. Doing the same with raw YAML requires keeping the previous manifests and reapplying them manually.

To remove a release entirely:

helm uninstall my-redis

This deletes all resources the chart created, plus the stored release record.

The chart structure

A chart is a directory laid out like this:

mychart/
  Chart.yaml          # name, version, appVersion, dependencies
  values.yaml         # default configuration values
  templates/
    deployment.yaml
    service.yaml
    configmap.yaml
    _helpers.tpl      # reusable template snippets
    NOTES.txt         # message shown after install
  charts/             # subcharts (dependencies)
  .helmignore

Chart.yaml is metadata. values.yaml is the configuration surface that operators of your chart see. Everything in templates/ is processed by Helm’s templating engine before being applied to the cluster.

A minimal Chart.yaml:

apiVersion: v2
name: mychart
description: An example chart
type: application
version: 0.1.0
appVersion: "1.0.0"

Templating with values

Inside templates/, files are Go templates with access to a .Values object populated from values.yaml and any overrides passed at install or upgrade time.

A trivial deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-app
  labels:
    app.kubernetes.io/name: {{ include "mychart.name" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ include "mychart.name" . }}
  template:
    metadata:
      labels:
        app: {{ include "mychart.name" . }}
    spec:
      containers:
        - name: app
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}
          env:
            {{- range $key, $value := .Values.env }}
            - name: {{ $key }}
              value: {{ $value | quote }}
            {{- end }}

The matching values.yaml:

replicaCount: 2
image:
  repository: myorg/app
  tag: "1.2.3"
service:
  port: 8080
env:
  LOG_LEVEL: info
  FEATURE_FLAG_X: "true"

At install time, anyone can override values:

helm install app ./mychart --set replicaCount=5 --set image.tag=1.2.4
# or
helm install app ./mychart -f production.values.yaml

The include and _helpers.tpl pattern lets you define reusable snippets — typically a name helper that respects release name length limits and a labels helper applied to every resource.

values.yaml as the API of a chart

The most important design choice when authoring a chart is the shape of values.yaml. It is the public interface other people interact with. Some rules of thumb:

  • Group related settings under namespaced keys: database.host, database.port, not dbHost, dbPort.
  • Provide sensible defaults so helm install works with no flags.
  • Document every key with a comment. The defaults file is the documentation.
  • Resist exposing every Kubernetes field — pick the ones operators actually need to change.

A small, well-thought-out values.yaml makes a chart easy to adopt. A sprawling one with hundreds of knobs makes upgrades terrifying.

Working with secrets and configmaps

Helm renders configmaps and secrets the same way it renders any other resource. If you are already using Kubernetes configmaps and secrets, you can drive them from values:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
data:
  {{- toYaml .Values.config | nindent 2 }}

For real secrets, do not put them in values.yaml and commit it. Common patterns:

  • Inject at install time via --set from a CI secret store.
  • Use helm-secrets with SOPS for encrypted-at-rest values files.
  • Reference an external secret manager via the External Secrets Operator and ignore Helm for the secret itself.

When to use Helm and when not

Helm shines when:

  • You need to distribute a multi-resource application to other teams or the public.
  • You run the same app in many environments with different configuration.
  • You want one-command rollbacks across many resources.
  • You depend on community charts for off-the-shelf software (Postgres, Prometheus, Redis).

It is less of a win when:

  • You have one simple app deployed by one team in one cluster, where kubectl apply -k with Kustomize is lighter.
  • You want strict, declarative GitOps with no client-side templating — tools like Argo CD with Kustomize or plain manifests fit better.
  • The templating starts to dominate the actual YAML, signaling that the configuration shape needs rethinking.

A pragmatic split: use community Helm charts for third-party software, and decide per-application whether your own services need Helm or just Kustomize-style overlays.

Wrap up

Helm packages Kubernetes manifests into versioned, parameterized, releasable units. Learn the install / upgrade / rollback loop first, then dig into chart authoring and the templating engine. Treat values.yaml as a public API and keep it small.

Combined with the resources covered in pods, deployments, and services and configmaps and secrets, Helm becomes the layer that turns a pile of YAML into something you can ship, share, and confidently roll back.