devops6 min read

Helm Tutorial: Learn Kubernetes Package Manager from Scratch (2026)

Helm Tutorial: Learn Kubernetes Package Manager from Scratch (2026)

Published:  |  Category: Devops  |  Reading time: ~15 min
Helm Tutorial: Learn Kubernetes Package Manager from Scratch (2026)

When I first saw a Kubernetes deployment manifest, I thought: this is great, but how do I parameterize it for different environments? Helm answered that question with charts, templates, and values. Helm is the package manager for Kubernetes — it bundles related YAML manifests into a single, versioned chart that you can install, upgrade, and rollback. It uses Go templates to inject configuration values, making the same chart deployable to dev, staging, and production with different settings. This tutorial covers Helm from chart creation to advanced dependency management, reflecting patterns I have used to package and distribute applications across dozens of clusters.

By the end of this tutorial, you will know how to create production-grade Helm charts, manage releases with rollbacks, use hooks for database migrations, and leverage the Helm ecosystem of public and private chart repositories.

Helm Architecture and Installation

Helm has two components: the client (helm CLI) and the library (Helm SDK). Helm 3 removed the server-side Tiller component for security — it now renders templates client-side and applies them via the Kubernetes API directly. This simplifies RBAC: the user or CI system needs only the permissions that the chart requires. Install Helm from the official script, package manager, or precompiled binary. The helm version command confirms the client and API version. I configure Helm with repository aliases for common chart sources: bitnami, stable, and my private chart museum. The helm repo update command refreshes the local cache of repository indexes. Helm stores release information in Secrets in the same namespace as the release, making release history inspectable with kubectl get secrets.

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo bitnami/nginx

Chart Structure and the Templates Directory

A chart is a directory with a standard layout: Chart.yaml (metadata), values.yaml (default configuration values), templates/ (Go template files that generate Kubernetes manifests), charts/ (subchart dependencies), and crds/ (CustomResourceDefinitions). The Chart.yaml file defines name, version, API version, description, and dependencies. Templates use Go template syntax with Sprig functions for string manipulation, type conversions, and flow control. The helm create command scaffolds a new chart with examples. I always remove the default templates and write my own to avoid unnecessary complexity. Template output can be previewed with helm template or helm install --dry-run, which renders the chart without installing it.

helm create mychart
tree mychart
# mychart/
# ├── Chart.yaml
# ├── charts/
# ├── templates
# │   ├── _helpers.tpl
# │   ├── deployment.yaml
# │   └── service.yaml
# └── values.yaml

Values, Templates, and Built-in Objects

Values are the primary mechanism for configuring charts. The values.yaml file provides defaults, and users override them at install time with --set, --values, or a separate values file. Built-in objects like .Release.Name, .Release.Namespace, .Chart.Name, and .Files give access to runtime metadata and chart file contents. Template functions like include, required, default, and toYaml handle data transformation. The _helpers.tpl file stores reusable template snippets using the define keyword. I use the required function for mandatory values so the chart fails immediately with a clear message if a user forgets to set a critical parameter. Pluck and dig functions from Sprig extract values from nested data structures safely.

# values.yaml
replicaCount: 3
image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent
service:
  port: 80
  type: ClusterIP
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}

Managing Releases: Install, Upgrade, Rollback

A Helm release is a single installation of a chart. The helm install command creates a new release; helm upgrade updates it to a new chart version or configuration. Helm tracks releases as Secrets, keeping a configurable number of historical revisions. The helm rollback command reverts to a previous revision — critical for production incident recovery. I use --atomic for upgrades: if the deployment fails to become healthy within a timeout, Helm automatically rolls back. The --history-max flag limits retained revisions to prevent Secret bloat. The helm list command shows all releases in the current namespace. For complex environments with many releases, I organize them into namespaces per team and use helm list --all-namespaces for a global view.

helm install myapp ./mychart --values prod-values.yaml
helm upgrade myapp ./mychart --set image.tag=v2.0.0 --atomic --timeout 5m
helm rollback myapp 3
helm history myapp
helm list -n production

Dependencies and Subcharts

Real-world applications depend on databases, caches, and message queues. Helm handles these as subcharts — child charts packaged inside the parent chart's charts/ directory. Dependencies are declared in Chart.yaml under the dependencies field. Running helm dependency update downloads the specified chart versions and builds a Chart.lock file for reproducible builds. Subchart values are nested under the subchart name in values.yaml. I use condition tags in dependencies to make subcharts optional: the parent chart can enable or disable PostgreSQL with a single boolean flag. Aliases resolve naming conflicts when multiple subcharts are different versions of the same chart. For team distribution, I publish subcharts to a private repository and reference them by version rather than bundling them.

# Chart.yaml
dependencies:
  - name: postgresql
    version: "~15.0.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
  - name: redis
    version: "~20.0.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled

Hooks, Tests, and Best Practices

Helm hooks are templates annotated with helm.sh/hook that run at specific points in a release lifecycle: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete. I use post-install hooks for database schema migrations, pre-delete hooks to drain connections before removing a service, and post-upgrade hooks to run smoke tests. Test hooks are annotated with helm.sh/hook: test-success or test-failure and run with helm test. Best practices include: always set resource limits, label resources consistently, use namespaces, pin chart versions in requirements, validate values with JSON Schema in values.schema.json, and document all values in README.md. The chart-testing tool runs linting and installation tests in CI pipelines.

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
  annotations:
    helm.sh/hook: post-install,post-upgrade
    helm.sh/hook-delete-policy: hook-succeeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: myapp/migrate:latest
          command: ["node", "migrate.js"]

Frequently Asked Questions

What is the difference between Helm and Kustomize?

Helm uses Go templates to generate YAML from parameterized values and is best for distributing packaged applications. Kustomize uses overlay patches on base YAML without templates and is best for environment-specific customization of your own manifests. Many teams use both: Helm to install third-party software and Kustomize to manage first-party application overlays.

How do I store Helm chart secrets in Git?

Use Helm Secrets or helm-secrets plugin with SOPS to encrypt values files. The encrypted file is committed to Git, and decryption happens at deploy time with a key from your CI system or local environment. Mozilla SOPS supports AWS KMS, GCP KMS, Azure Key Vault, and age for encryption.

Can Helm manage CRDs?

Yes. CRDs in the crds/ directory are installed before template rendering, which is essential for charts that depend on Custom Resources. CRDs are not upgraded or deleted during release lifecycle management — you must manage CRD updates separately to avoid accidental data loss.

What happens if helm upgrade fails?

Without --atomic, a failed upgrade leaves the release in a failed state with the new revision marked as failed. You can rollback to the previous revision. With --atomic, Helm automatically rolls back to the previous revision if the deployment does not become healthy within the timeout. Always use --atomic for CI/CD pipelines.

Originally published on Ayodhyyya. Last updated June 1, 2026.