Helm - Package manager

Arindam BaidyaArindam Baidya
31 min read

Introduction to Helm

Helm is a package manager for Kubernetes that simplifies deploying, managing, and upgrading applications using reusable templates called charts. It lets you define infrastructure as code, customize deployments with values, and easily roll back or upgrade versionsβ€”making Kubernetes app management faster, repeatable, and more consistent.

πŸ”„ Helm 2 vs Helm 3 – Key Differences

FeatureHelm 2Helm 3
Tillerβœ… Used a server-side component called Tiller to manage releases in the cluster. This added complexity and raised security concerns (e.g., cluster-wide access).❌ Tiller removed. Helm 3 runs client-side, using the user's kubeconfig for direct accessβ€”simpler and more secure.
SecurityRequired complex RBAC configuration to secure Tiller.More secure by designβ€”Helm respects Kubernetes native access control.
Release ManagementReleases tracked by Tiller, could list all cluster-wide.Releases are now namespaced, aligning with Kubernetes best practices.
Upgrade StrategyUsed a two-way merge, often overwriting changes made manually on the cluster.Uses a three-way strategic merge patch, which compares the live state, previous release, and new configβ€”minimizes conflicts and preserves manual changes.
RevisionsCommands like helm upgrade created new revisions, but rollback behavior could be unpredictable.Revisions are managed more cleanly:β€’ helm install wordpress β†’ Revision 1β€’ helm upgrade wordpress β†’ Revision 2β€’ helm rollback wordpress β†’ Revision 3 (but content is same as Revision 1)
CRDs (Custom Resource Definitions)Managed like normal resources, often causing issues during upgrades.Supports a dedicated crds/ directory for safer, idempotent CRD installation.
OCI Support❌ Not available.βœ… Helm 3 supports storing and retrieving charts from OCI-compliant registries (like Docker Hub).

βœ… Summary

Helm 3 addresses the architectural and security limitations of Helm 2 by removing Tiller, implementing a three-way strategic merge for safer upgrades, and aligning more closely with Kubernetes best practices. It’s the current and recommended version for production use.

🧭 Helm Components

1. Helm Command Line Utility (CLI)

Helm is operated through a command-line tool installed on the local system. This utility is used to execute various Helm actions such as installing a chart, upgrading an existing application, rolling back to a previous version, and managing deployments in a Kubernetes cluster. It interacts directly with the Kubernetes API and plays a crucial role in managing the lifecycle of applications deployed using Helm charts.


2. Charts

Helm uses charts to package applications. A chart is a structured collection of files that contains all the necessary instructions and definitions (in YAML format) for deploying a group of Kubernetes resources. These may include deployments, services, ConfigMaps, secrets, and more. Charts simplify application deployment by encapsulating complex configurations into reusable, versioned packages.

Helm charts are typically available from public repositories like ArtifactHub.io, which hosts thousands of charts maintained by the community (e.g., Bitnami, TrueCharts, Appscode, Community Operators). Users can download and use these charts without having to build them from scratch.


3. Templates and the values.yaml File

Inside a Helm chart, the core of customization lies in the templating mechanism. Rather than hardcoding values such as image names, replica counts, or environment variables into the deployment YAML, Helm uses Go-based templates. These templates dynamically pull their values from a file called values.yaml.

For example, in a basic Nginx-based web server application that includes a deployment and a service, the image name and number of replicas are not directly written in the deployment file. Instead, they are templated and sourced from values.yaml.

The values.yaml file serves as the configuration input for the chart. It contains the default values for all configurable parameters of the application. In most real-world cases, this is the only file that users need to edit in order to customize their application deployment. It acts as the "settings file" that makes Helm charts reusable and easily configurable.


4. Releases and Revisions

When a Helm chart is installed into a Kubernetes cluster (e.g., helm install my-site bitnami/wordpress), a release is created. A release is a named, running instance of a Helm chart deployed in a specific namespace of the Kubernetes cluster. You can deploy multiple releases of the same chart by giving each one a unique name. This flexibility allows teams to run several environments (e.g., dev, staging, prod) using the same chart.

Every time a release is upgraded or rolled back, Helm creates a new revision. These revisions are like snapshots that track the state of the application at a certain point in time:

  • helm install wordpress creates revision 1

  • helm upgrade wordpress creates revision 2

  • helm rollback wordpress creates revision 3, but it reverts to the state from revision 1

Helm 3 improves this process using a three-way strategic merge patch, which compares the live state, the old manifest, and the new manifest. This allows for safer upgrades by preserving manual changes made to the cluster.


5. Metadata Management

To track what charts have been installed, what configurations were used, and what revision state each release is in, Helm stores metadata β€” essentially, data about the deployments. This metadata is stored directly inside the Kubernetes cluster using Kubernetes Secrets. These Secrets are accessible by users who have access to the cluster and ensure that deployment history is preserved for as long as the cluster exists. This design allows for better visibility and auditing of deployments within the project.


6. Why Use Helm Charts from Public Repos?

Most of the time, users do not need to write Helm charts from scratch because well-tested and customizable charts already exist in public repositories like:

  • Bitnami

  • TrueCharts

  • Appscode

  • Community Operators

  • Available collectively on ArtifactHub.io

These charts are designed with flexibility in mind and expose a rich set of parameters via values.yaml, enabling users to tailor the deployment to their specific requirements without altering the chart templates directly.

πŸ“˜ Helm Charts – Chart.yaml File Explained

In every Helm chart, aside from the values.yaml file used for configuration, there is a mandatory file named Chart.yaml. This file contains metadata about the chart itself and plays a crucial role in chart identification, versioning, and dependency management.


πŸ“„ Purpose of Chart.yaml

The Chart.yaml file provides essential metadata for a Helm chart. Helm uses this file to understand what the chart is, how it relates to applications, and how it should be processed during packaging, installation, or upgrade.


🧩 Key Fields in Chart.yaml

FieldDescription
apiVersionSpecifies the version of the chart API being used.
- v1: for Helm 2
- v2: for Helm 3
nameThe name of the chart. This is the unique identifier for the chart within a repository.
versionThe version of the chart itself, separate from the application version. It is used for tracking updates to the chart.
appVersionThe version of the actual application being deployed by this chart.
descriptionA short summary describing what this chart does or which application it deploys.
typeIndicates the type of chart:
– application (default): Charts that deploy applications.
– library: Charts that provide helper templates and functions, used by other charts.
keywordsA list of relevant keywords associated with the chart. Useful for search and discovery in chart repositories.
home (optional)A URL to the homepage of the project or application.
icon (optional)A URL to an icon image representing the application. Helpful in GUI-based Helm dashboards.
maintainersA list of individuals or teams responsible for maintaining the chart. Includes their names and optionally email/contact info.
dependenciesLists other charts that this chart depends on. For example, a WordPress chart may declare a dependency on a MariaDB chart. This allows Helm to handle both installations together without manually merging manifest files.

πŸ“¦ Example Use Case: WordPress with MariaDB

In a Helm chart for deploying WordPress:

  • Instead of manually combining WordPress and MariaDB YAML manifests, you can declare MariaDB as a dependency in Chart.yaml.

  • This way, Helm will automatically fetch and deploy MariaDB alongside WordPress when the chart is installed.

Helm chart structure


πŸ“˜ Helm CLI – Core Commands & Usage

Helm simplifies application deployment on Kubernetes by offering powerful command-line utilities. Here's a breakdown of essential Helm commands and concepts for working with repositories and deploying charts.


🧰 Basic Helm CLI Help

  • helm --help:
    Displays general help about Helm commands, structure, and usage.

  • helm repo --help:
    Provides help related to Helm repository management commands like adding, listing, and updating chart repos.

  • helm repo update --help:
    Shows information about how to update your local list of chart repositories (similar to apt-get update for package managers).


🌐 Artifact Hub (artifacthub.io)

  • Artifact Hub is a public platform to browse and discover Helm charts created by the community and vendors.

  • It contains charts from well-known publishers like Bitnami, TrueCharts, Appscode, and many others.

  • You can search, review, and get installation instructions for charts directly from the site.

From the Artifact Hub, you can copy Helm commands to install a chart directly into your cluster.


πŸ”Ž Searching for Charts

Helm allows you to search for charts using the CLI:

  1. Search in Artifact Hub via CLI:

     helm search hub wordpress
    
    • Searches charts in Artifact Hub (not just what's on your local machine).

    • Requires internet access and Helm 3.1+.

  2. Search in Added Repositories:

     helm search repo wordpress
    
    • Searches only within your locally added Helm repositories.

πŸ“₯ Adding and Using Helm Repositories

Before installing a chart, you often need to add the repo that contains it:

  • Add a Helm chart repository (e.g., Bitnami):

      helm repo add bitnami https://charts.bitnami.com/bitnami
    
  • Update your local repo index:

      helm repo update
    

    This downloads the latest chart information, just like sudo apt-get update.

  • List all added repos:

      helm repo list
    

πŸš€ Installing and Managing Releases

  • Install a chart (e.g., WordPress from Bitnami):

      helm install my-release bitnami/wordpress
    
    • This installs the chart as a release named my-release.

    • Helm will deploy all necessary Kubernetes objects (e.g., Deployments, Services, PVCs).

  • List all deployed releases:

      helm list
    
  • Uninstall a release:

      helm uninstall my-release
    
    • This removes the deployed application and all its associated Kubernetes resources.

βœ… Summary

Deploying an application on a Kubernetes cluster has never been easier than with Helm.

Helm streamlines:

  • Finding and managing charts

  • Installing and configuring applications

  • Updating and rolling back deployments

With repositories like Bitnami and tools like Artifact Hub, Helm provides a robust ecosystem for production-grade application management on Kubernetes.


βš™οΈ Customizing Helm Chart Parameters

By default, Helm installs applications using values from the chart’s built-in values.yaml file. However, you often need to override or customize these values to tailor the deployment to your environment.

Helm provides three main ways to customize chart parameters:


πŸ”§ 1. Override Parameters via Command Line (--set)

You can directly override values from the default values.yaml using the --set flag during installation.

Example:

helm install --set wordpressBlogName="Helm Tutorials" my-release bitnami/wordpress
  • This overrides the wordpressBlogName field.

  • It’s quick and easy for simple one-off changes.

  • For multiple values, use:

      helm install --set key1=value1,key2=value2 my-release bitnami/wordpress
    

πŸ“„ 2. Use a Custom values.yaml File (--values)

For more complex or structured configurations, it's better to create a custom YAML file with all your overrides and pass it during installation.

Example:

helm install --values custom-values.yaml my-release bitnami/wordpress
  • Keeps your custom settings separate.

  • Easier to version-control and reuse.

  • Highly recommended for team-based or production deployments.


πŸ› οΈ 3. Modify the Original Chart Files

If you want full control, including editing templates or default values.yaml, you can pull and extract the chart locally.

Steps:

helm pull bitnami/wordpress
helm pull --untar bitnami/wordpress
  • This creates a local wordpress/ directory with the full chart source.

  • You can now:

    • Edit the values.yaml file directly.

    • Modify templates inside the templates/ folder.

Then install from the local chart:

helm install my-release ./wordpress
  • Use this method when:

    • You need to deeply customize logic or structure.

    • You want to build your own version of a chart.


βœ… Summary

MethodDescriptionBest For
--setOverride specific values inlineQuick changes
--values custom.yamlUse a separate config fileReusability, cleaner setup
Modify pulled chartFull customization of values and templatesAdvanced use, custom charts

⚠️ Always review the chart’s default values.yaml (via helm show values bitnami/wordpress) to know what options are available for customization.


πŸ› οΈ Helm Lifecycle Management

πŸ“¦ What is a Helm Release?

  • A release is a deployed instance of a chart.

  • It represents a package of Kubernetes objects.

  • Helm tracks all resources belonging to a release, so it can:

    • Upgrade

    • Rollback

    • Uninstall
      without affecting other releases.


πŸš€ Helm Install Examples:

helm install my-site bitnami/wordpress
helm install my-second-site bitnami/wordpress
  • Each command creates a separate release even if from the same chart.

🧩 Installing Specific Versions:

helm install nginx-release bitnami/nginx --version 7.1.0
  • Deploys a specific chart version.

πŸ”„ Upgrading a Release:

helm upgrade nginx-release bitnami/nginx
  • Creates a new revision (v2).

  • Helm tracks the revision history.


πŸ“œ View Lifecycle Details:

helm list                          # List all releases
helm history nginx-release         # Show revisions with metadata
kubectl get pods                   # Get pod names
kubectl describe pod <pod-name>   # View image info

βͺ Rollback:

helm rollback nginx-release 1
  • Restores configuration from revision 1.

  • Creates a new revision, not literally going back.

  • Note: App data (e.g., DB contents) is not restored.


⚠️ Upgrade Issues:

If upgrade fails due to permission errors (e.g., DB credentials):

  • Use Helm CLI flags as recommended in error messages.

  • Helm may need access to admin credentials (DB, WordPress).


🧠 Important Notes:

  • Helm manages manifests/declarations, not runtime data.

  • Rollbacks restore config/state, not data volumes or files created by the app.

Helm Charts Anatomy

πŸ”§ Helm Charts Overview

Helm charts are incredibly versatile and can automate almost any kind of Kubernetes package installation that we can think of. Although charts are technically not programs, they can act like programs.

Besides just installing various Kubernetes objects to get our app up and running, they can do some extra stuff too. For example, we can write the chart in such a way that whenever we do a Helm upgrade, a database can be automatically backed up before that happens. So we have a way to restore data from backup in case something goes wrong.


πŸ› οΈ Creating a Helm Chart from Scratch

Suppose we have a helloworld Deployment and Service YAML file.

1. Create a Helm Chart:

$ helm create nginx-chart

2. Edit Chart Metadata:

$ cd nginx-chart
$ vi Chart.yaml

Sample Chart.yaml:

apiVersion: v2
name: nginx-chart
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "1.16.0"
maintainers:
  - email: john@example.com
    name: john smith

3. Remove Sample Templates:

$ rm -r templates/*

4. Add Your Own YAML Files:

Place your existing deployment.yaml and service.yaml into the templates/ directory.

Structure:

nginx-chart/
β”œβ”€β”€ Chart.yaml
β”œβ”€β”€ values.yaml
└── templates/
    β”œβ”€β”€ deployment.yaml
    └── service.yaml

5. Example Service and Deployment Files:

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: hello-world

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
        - name: hello-world
          image: nginx
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

⚠️ Problem with Static Values

These files contain static values:

  • Name of deployment (hello-world)

  • Number of replicas

  • Container name

Scenario:

$ helm install hello-world-1 ./nginx-chart

Then:

$ kubectl get deployment
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
hello-world   0/2     2            0           24s

Trying another install with different release name:

$ helm install hello-world-2 ./nginx-chart

❌ Error:

Deployment "hello-world" in namespace "default" exists and cannot be imported into the current release: 
invalid ownership metadata; annotation validation error: 
key "meta.helm.sh/release-name" must equal "hello-world-2": current value is "hello-world-1"

Root cause: The name hello-world is hardcoded in the YAML files and causes a conflict when deploying multiple releases.

🧩 Helm Templating – Solving the Static Values Problem

⚠️ Problem Recap

  • Static values like deployment names (hello-world) in the YAML files cause errors when installing the same chart with different release names:

      $ helm install hello-world-1 ./nginx-chart
      $ helm install hello-world-2 ./nginx-chart
      Error: rendered manifests contain a resource that already exists.
    

βœ… Solution: Templatize the Chart

🎯 Purpose of Templating

  • Templating allows dynamic rendering of values based on:

    • Release information (name, namespace, etc.)

    • Chart metadata

    • User-defined values from values.yaml

    • Cluster capabilities


πŸ”§ Helm Built-in Objects

🧷 .Release

  • .Release.Name β†’ Name of the release (e.g., hello-world-1)

  • .Release.Namespace

  • .Release.IsInstall

  • .Release.IsUpgrade

  • .Release.Revision

  • .Release.Service

πŸ“¦ .Chart

  • .Chart.Name

  • .Chart.ApiVersion

  • .Chart.Version

  • .Chart.Type

  • .Chart.Keywords

  • .Chart.Home

πŸ” .Capabilities

  • .Capabilities.KubeVersion

  • .Capabilities.ApiVersions

  • .Capabilities.HelmVersion

  • .Capabilities.GitCommit

  • .Capabilities.GitTreeState

  • .Capabilities.GoVersion

βš™οΈ .Values

  • User-defined values from values.yaml

  • Example:

      replicaCount: 2
      image:
        repository: nginx
        pullPolicy: IfNotPresent
        tag: "1.16.0"
    

πŸ› οΈ Example Templatization in YAML Files

βœ… Deployment Template (templates/deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-nginx
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-nginx
    spec:
      containers:
        - name: {{ .Release.Name }}-nginx
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

βœ… Service Template (templates/service.yaml)

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-svc
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: {{ .Release.Name }}-nginx

πŸ—‚οΈ values.yaml Example

# Default values for nginx-chart.
replicaCount: 2
image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: "1.16.0"

πŸ’‘ Key Notes

  • .Release, .Chart, and .Capabilities follow CapitalCase because they are built-in.

  • .Values follow lower-case (or user-defined) because they are user-defined.

  • When we install a Helm chart, Helm merges:

    • Templates

    • Release-specific info

    • Chart metadata

    • Values.yaml

β†’ to render final Kubernetes manifest files.


πŸ§ͺ Final Test

$ helm install hello-world-1 ./nginx-chart
$ helm install hello-world-2 ./nginx-chart
$ kubectl get deployment

Result:

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
hello-world-1-nginx   1/2     2            1           8s
hello-world-2-nginx   0/2     2            0           4s

Now, both releases work independently without conflict.


βœ… Making Sure a Helm Chart Works as Intended

Before installing a Helm chart into your Kubernetes cluster, it's crucial to verify its correctness using 3 main techniques:


πŸ” 1. Linting the Chart

Purpose: Detect syntax or structural issues in the chart and YAML files.

  • Command:

      helm lint ./nginx-chart
    
  • What it does:

    • Validates the chart structure.

    • Detects template syntax errors (like wrong object references).

    • Flags YAML formatting issues.

  • Example Error Output:

      [ERROR] templates/: template: nginx-chart/templates/deployment.yaml:4:19: 
      executing "nginx-chart/templates/deployment.yaml" at <.Releese.Name>: 
      nil pointer evaluating interface {}.Name
    

    (Note the typo .Releese.Name β†’ should be .Release.Name)


πŸ§ͺ 2. Template Rendering

Purpose: Renders chart templates locally and lets you see the final manifest output.

  • Command:

      helm template ./nginx-chart
    
  • With debug for more detail:

      helm template ./nginx-chart --debug
    
  • Use Case: Helpful for catching logic or formatting issues, like indentation or template logic problems.

  • Example Debug Output:

      Error: YAML parse error on nginx-chart/templates/deployment.yaml: 
      error converting YAML to JSON: yaml: line 5: mapping values are not allowed in this context
    
  • Important: This will not catch Kubernetes-specific semantic errors (e.g., using container instead of containers).


πŸ§ͺ 3. Dry Run Installation

Purpose: Simulates a full install to ensure Kubernetes would accept the manifests.

  • Command:

      helm install hello-world-1 ./nginx-chart --dry-run
    
  • What it does:

    • Runs full rendering and validation as if installing to the cluster.

    • Checks for errors that would occur at runtime in Kubernetes.

  • Best for:

    • Catching things like typos in Kubernetes manifest fields (container vs containers).

    • Verifying that all resources would deploy correctly.


πŸ“Œ Summary of Template Values Used

Object TypeExample KeyExample Value
.Release.Name{{ .Release.Name }}hello-world-1
.Values.replicaCount{{ .Values.replicaCount }}2
.Values.image.repository{{ .Values.image.repository }}nginx

πŸ“ File Example Snippets

values.yaml:

replicaCount: 2
image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: "1.16.0"

deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
        - name: hello-world
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-svc
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: hello-world

By using lint, template, and --dry-run in combination, you can be confident that your Helm charts are well-formed, valid, and ready for Kubernetes deployment.


πŸ“¦ Helm Template Functions & Deployment Notes

πŸ› οΈ Helm Template Usage in deployment.yaml

Helm uses Go templating to inject dynamic values into Kubernetes manifests. Examples:

metadata:
  name: {{ .Release.Name }}-nginx
spec:
  replicas: {{ .Values.replicaCount }}
  containers:
    - name: hello-world
      image: {{ .Values.image.repository }}

πŸ“ Helm Chart Files Breakdown

βœ… values.yaml – Default Configurations

replicaCount: 2
image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: "1.16.0"

βœ… templates/deployment.yaml – Templatized Manifest

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
        - name: hello-world
          image: {{ .Values.image.repository }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

πŸ”  Helm Templating: String Functions

Helm provides powerful built-in functions for string manipulation:

FunctionExampleOutput
upper{{ upper .Values.image.repository }}"NGINX"
quote{{ quote .Values.image.repository }}"nginx"
replace "x" "y"{{ replace "x" "y" .Values.image.repository }}"nginy"
shuffle{{ shuffle .Values.image.repository }}e.g. "xignn"

πŸ“˜ Full List:
Helm Template Function List β†’ String Functions

Other categories:

  • Cryptographic & Security

  • File Path

  • Lists

  • Dictionaries

  • Kubernetes

  • Logic, Math, Regex, Encoding, URL, Type Conversion, etc.


⚠️ Common Pitfall: InvalidImageName

When running:

kubectl get pods

You may see:

NAME                                READY   STATUS             RESTARTS   AGE
nginx-deployment-xxxx              0/1     InvalidImageName   0          3s

This happens if the image path is incorrect or misconfigured. Helm templates won’t catch this β€” only Kubernetes will.

βœ… Always verify .Values.image.repository resolves to a valid image string like nginx:1.16.0.


πŸ”§ Using Default Values

If a value isn't passed, use default:

image: {{ default "nginx" .Values.image.repository }}

πŸ” Helm Pipelines & String Functions

πŸ§ͺ Pipelines in Helm

Just like in Linux:

$ echo "abcd"
abcd

$ echo "abcd" | tr a-z A-Z
ABCD

Helm templates also allow pipelining β€” chaining multiple functions together:


πŸ”  Helm String Functions (Examples)

1️⃣ Convert to UPPERCASE

{{ upper .Values.image.repository }}

If the value is:

image:
  repository: nginx

It becomes:

NGINX

2️⃣ Using | (pipe) for cleaner syntax

{{ .Values.image.repository | upper }}

Output:

NGINX

3️⃣ Add quotes using quote

{{ .Values.image.repository | upper | quote }}

Output:

image: "NGINX"

4️⃣ Add shuffle for random character order (demo)

{{ .Values.image.repository | upper | quote | shuffle }}

Output (varies randomly):

image: "GNXNI"

(Helm will output a random arrangement of "NGINX" each time)


πŸ› οΈ Helm Conditionals and values.yaml Explained

πŸ“Œ What is values.yaml?

  • The values.yaml file defines default configuration values used in Helm templates.

  • These values are referenced in Helm templates using the {{ .Values.<key> }} syntax.

Example:

# values.yaml
replicaCount: 2
image: nginx
orgLabel: payroll
serviceAccount:
  create: true

πŸ“„ Example Helm Template (service.yaml):

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-nginx
  {{- if .Values.orgLabel }}
  labels:
    org: {{ .Values.orgLabel }}
  {{- end }}
spec:
  ports:
    - port: 80
      name: http
  selector:
    app: hello-world

βœ… Conditional Logic in Helm

Helm templates support Go templating with conditional logic like:

{{- if .Values.orgLabel }}
  labels:
    org: {{ .Values.orgLabel }}
{{- else if eq .Values.orgLabel "hr" }}
  labels:
    org: human resources
{{- else }}
  labels:
    org: default
{{- end }}

πŸ”£ Functions in Helm Conditionals

FunctionPurpose
eqEquals
neNot equal
ltLess than
leLess than or equal
gtGreater than
geGreater than or equal
notNegation
emptyChecks if a value is empty

πŸ§ͺ Example: serviceaccount.yaml Conditional Creation

# values.yaml
serviceAccount:
  create: true
# serviceaccount.yaml
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Release.Name }}-robot-sa
{{- end }}

πŸ”§ Helm Templating: Using with for Scope Control

In Helm templates, {{ with ... }} is used to narrow the scope to a specific section of values, reducing redundancy and improving readability. When you use with, the context (.) inside the block becomes the specified object.


πŸ—‚οΈ Example: values.yaml Structure

app:
  ui:
    bg: red
    fg: black
  db:
    name: "users"
    conn: "mongodb://localhost:27020/mydb"

πŸ“„ Without with: Full Path Required

data:
  background: {{ .Values.app.ui.bg }}
  foreground: {{ .Values.app.ui.fg }}
  database: {{ .Values.app.db.name }}
  connection: {{ .Values.app.db.conn }}

βœ… With with: Cleaner & Scoped

{{- with .Values.app }}
  {{- with .ui }}
    background: {{ .bg }}
    foreground: {{ .fg }}
  {{- end }}
  {{- with .db }}
    database: {{ .name }}
    connection: {{ .conn }}
  {{- end }}
{{- end }}

Here, .bg, .fg, .name, and .conn are used instead of the full path, because scope is now limited to app, ui, and db respectively.


🧨 Common Error

Error: template: ... at <.Release.Name>: nil pointer evaluating interface {}.Name

Cause: You are outside the original root scope (.) when accessing .Release.Name.
Fix: Use $ to refer to the root context:

release: {{ $.Release.Name }}

πŸ” Helm range – Looping Over Lists

πŸ“‚ Example values.yaml

regions:
  - ohio
  - newyork
  - ontario
  - london
  - singapore
  - mumbai

πŸ“„ Example configmap.yaml Template

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-regioninfo
data:
  regions:
{{- range .Values.regions }}
    - {{ . | quote }}
{{- end }}

🧠 What’s Happening:

  • range .Values.regions loops through each value in the regions list.

  • . inside the loop refers to the current region (e.g., "ohio").

  • quote adds double quotes around each region name.

  • {{- and -}} remove whitespace for cleaner YAML.


βœ… Rendered Output

If .Release.Name = demo, then:

Version: v1
kind: ConfigMap
metadata:
  name: demo-regioninfo
data:
  regions:
    - "ohio"
    - "newyork"
    - "ontario"
    - "london"
    - "singapore"
    - "mumbai"

🧾 Named Templates in Helm

Named templates in Helm are reusable blocks that help avoid duplication in Kubernetes manifest files. These templates are commonly stored in _helpers.tpl.


πŸ“¦ Define a Template in _helpers.tpl

{{- define "labels" }}
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
{{- end }}

This defines a template block called "labels".


πŸ“„ service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    {{- template "labels" . }}
spec:
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: hello-world

βœ… include is used with | indent 4 to correctly align the template output under labels.


πŸ“„ deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    {{- template "labels" . }}
spec:
  selector:
    matchLabels:
    {{- include "labels" . | indent 2 }}
  template:
    metadata:
      labels:
    {{- include "labels" . | indent 4 }}
    spec:
      containers:
        - name: nginx
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
  • template is used when no piping or indentation is needed.

  • include is used when output needs to be indented to fit YAML structure.


πŸ” Why Use . (Dot Operator)?

  • The . (dot) passes the current context (e.g., .Release.Name) to the named template.

  • Without ., the template cannot access Helm values like .Values, .Chart, or .Release.

βœ… Correct:

{{ include "labels" . }}
{{ template "labels" . }}

❌ Incorrect:

{{ include "labels" }}

🧩 Helm Chart Hooks

Helm hooks allow us to inject custom lifecycle events during chart operations (install, upgrade, delete, rollback), enabling advanced actions like:

  • Backing up a database before an upgrade

  • Displaying a site-wide announcement during upgrades

  • Sending email alerts during install or delete


πŸ› οΈ Example Use Case: Backup Database Before Upgrade

Imagine you have a script called backup.sh that backs up your database. You want Kubernetes to execute this once before each upgrade.

To run it only once (instead of continuously like a Pod), use a Kubernetes Job, and configure it with a Helm hook.


βš™οΈ Helm Lifecycle and Hook Events

Typical lifecycle during a helm upgrade:

helm upgrade β†’ verify β†’ render β†’ upgrade

With hooks, you can insert steps like:

  • pre-install

  • post-install

  • pre-upgrade

  • post-upgrade

  • pre-delete

  • post-delete

  • pre-rollback

  • post-rollback


πŸ”§ Hook Configuration Example (backup-job.yaml)

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-pre-upgrade-backup
  annotations:
    "helm.sh/hook": pre-upgrade
    "helm.sh/hook-weight": "5"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    metadata:
      name: backup-database
    spec:
      restartPolicy: Never
      containers:
        - name: pre-upgrade-backup-job
          image: alpine
          command: ["/bin/sh", "-c", "/bin/backup.sh"]

πŸ“ File Placement

Place backup-job.yaml inside your chart’s templates/ directory (like any other template file). Even though it's there, Helm won’t treat it like a regular deployment due to the hook annotation.


πŸ”„ How Multiple Hooks Are Ordered

If multiple hooks exist for the same event (pre-upgrade, for instance):

  • They are sorted by hook-weight (lowest first).

  • If weights are the same:

    • Sorted by resource kind (alphabetical)

    • Then by resource name

This allows precise control over the execution order, which is critical when different jobs depend on one another.


🧹 Hook Resource Cleanup

Without cleanup, Kubernetes will keep the Job in the cluster after it runs. To avoid clutter or name collisions:

Use:

"helm.sh/hook-delete-policy": hook-succeeded

Other valid values:

  • hook-failed

  • before-hook-creation

This ensures each hook resource is removed after it completes (or before the next run).


βœ… Summary

FeaturePurpose
helm.sh/hookDefines when the hook runs
helm.sh/hook-weightControls execution order
helm.sh/hook-delete-policyDeletes hook resources after execution
Job (vs Pod)One-time script execution

πŸ“¦ Typical Workflow Example

$ helm upgrade myapp ./mychart
β†’ Helm sees a pre-upgrade hook in backup-job.yaml
β†’ Runs the backup.sh script inside a Job
β†’ Waits for it to succeed
β†’ Proceeds with the upgrade
β†’ (Optional) Runs post-upgrade tasks (e.g., send email, remove banner)

🧾 Question

You have previously installed a Helm chart for a web application. Now, perform the following tasks:

  1. Create a new Kubernetes Job named upgrade-hook.

  2. This job should be configured as a Helm hook that runs after the chart is upgraded (i.e., use the post-upgrade hook).

  3. The container image to be used should be alpine.

  4. The job should output the message: Successful Upgradation.

  5. Update the Helm chart with:

    • App version: 2.4.51

    • Chart version: 0.2

  6. Finally, upgrade the existing Helm release named httpd using the updated chart.


βœ… Solution

πŸ“ Step 1: Update Chart.yaml

Open the Chart.yaml file of your Helm chart (e.g., webapp/Chart.yaml) and update it as follows:

apiVersion: v2
name: webapp
description: An Apache Application
type: application
version: 0.2       # Chart version
appVersion: 2.4.51 # App version

πŸ“ Step 2: Create the Hook Job (upgrade-job.yaml)

Inside your chart's templates/ directory, create a file named upgrade-job.yaml with the following content:

apiVersion: batch/v1
kind: Job
metadata:
  name: upgrade-hook
  annotations:
    "helm.sh/hook": post-upgrade
spec:
  template:
    spec:
      containers:
        - name: upgrade-hook
          image: alpine
          command: ["echo", "Successful Upgradation"]
      restartPolicy: Never
  backoffLimit: 4

βœ… This job is configured to run only after a Helm upgrade, and it simply prints a success message.


πŸ’» Step 3: Upgrade the Helm Release

Now, run the following command to upgrade your Helm release:

helm upgrade httpd webapp/

This will:

  • Upgrade the chart named webapp

  • Execute the upgrade-hook job after the upgrade (due to the post-upgrade hook)


βœ… (Optional) Verify Job Output

To check the output of the upgrade-hook job:

kubectl get jobs
kubectl logs job/upgrade-hook

πŸ“¦ Helm Chart Packaging, Signing, and Verification


βœ… 1. Packaging a Helm Chart

Purpose:
To compress your chart directory into a distributable .tgz archive.

Command:

helm package ./nginx-chart

Result:

  • A packaged chart file is created, e.g., nginx-chart-0.1.0.tgz

  • The version 0.1.0 is defined inside the chart’s Chart.yaml

  • The .tgz is a tarball compressed with gzip used for storage, distribution, and installation.


πŸ”’ 2. Why Sign Charts?

Purpose:
To ensure the integrity and authenticity of the chart.

Scenario:

  • Imagine a user downloads a chart from a Helm repository.

  • If the repository is compromised or tampered with, malicious content may be delivered.

  • Signing allows the user to verify that:

    • The chart has not been modified

    • It was created by a trusted developer

How it works:

  • You sign your chart using your private GPG key.

  • This creates a .prov file that includes:

    • The SHA256 hash of the .tgz

    • Your GPG signature


πŸ” 3. Creating a GPG Key

GPG (GNU Privacy Guard) is used for generating cryptographic keys.

Method 1: Quick (for testing or short-term use)

gpg --quick-generate-key "John Smith"
  • Automatically creates a key pair with default settings.
gpg --full-generate-key
  • Offers custom configuration:

    • Key type (RSA, ECC)

    • Expiration date

    • Email address

    • Passphrase

Export Secret Key for Helm (Optional):

gpg --export-secret-keys > ~/.gnupg/secring.gpg

πŸ”Έ Helm uses the secring.gpg file to locate your private key.


πŸ–ŠοΈ 4. Signing the Helm Chart

Command:

helm package --sign --key 'John Smith' --keyring ~/.gnupg/secring.gpg ./nginx-chart

What it does:

  • Packages the chart and adds a .prov file

  • Uses your private key to sign the chart

  • The .prov file includes:

    • Chart metadata

    • SHA256 hash

    • GPG signature block

πŸ”Έ The .prov file must be distributed along with the .tgz.


πŸ“œ 5. Understanding the Provenance (.prov) File

This file contains:

  1. Metadata from Chart.yaml

  2. SHA256 hash of the chart archive

  3. The GPG signature that proves the authenticity of the signer

You can view the SHA256 hash of the chart like this:

sha256sum nginx-chart-0.1.0.tgz

πŸ§ͺ 6. Verifying a Signed Chart

Before verification, ensure you have the public key of the chart signer.

Step 1: Export the public key from your GPG setup

gpg --export 'John Smith' > mypublickey

Step 2: Verify the signed chart

helm verify --keyring ./mypublickey ./nginx-chart-0.1.0.tgz

Output (if successful):

Signed by: John Smith
Chart Hash Verified: sha256:...

πŸ”Έ This proves the chart came from John Smith and has not been modified.


🌍 7. Uploading to a Repository

When distributing your Helm chart:

  • Upload both:

    • nginx-chart-0.1.0.tgz (chart archive)

    • nginx-chart-0.1.0.tgz.prov (provenance/signature file)

Your users should also be able to obtain your public GPG key, which they can import like this:

gpg --recv-keys --keyserver keyserver.ubuntu.com <KEY_ID>

πŸš€ 8. Installing a Chart with Signature Verification

Users can install a chart and verify its signature during installation:

helm install --verify nginx-chart ./nginx-chart-0.1.0.tgz

πŸ”Έ This installation will fail if:

  • The .prov file is missing

  • The public key is not trusted or missing

  • The chart has been tampered with


🧾 Example Workflow Summary

# Step 1: Package the chart
helm package ./nginx-chart

# Step 2: Generate GPG key (if not done)
gpg --quick-generate-key "John Smith"

# Step 3: Export secret key (for Helm)
gpg --export-secret-keys > ~/.gnupg/secring.gpg

# Step 4: Sign the chart
helm package --sign --key 'John Smith' --keyring ~/.gnupg/secring.gpg ./nginx-chart

# Step 5: Export public key (for others to verify)
gpg --export 'John Smith' > mypublickey

# Step 6: Verify the signed chart
helm verify --keyring ./mypublickey ./nginx-chart-0.1.0.tgz

# Step 7: Install with verification
helm install --verify nginx-release ./nginx-chart-0.1.0.tgz

πŸ“¦ Uploading Helm Charts and Using a Custom Chart Repository

Helm allows us to package and distribute charts via a custom chart repository, similar to how package managers like npm or pip operate. Below is a step-by-step guide on how to prepare and host your own Helm charts using index.yaml, and then use them in deployments.


πŸ”§ Step 1: Organize Chart Files

First, we package and collect our chart files (including the provenance file if available):

$ ls
nginx-chart/  nginx-chart-0.1.0.tgz  nginx-chart-0.1.0.tgz.prov

Create a dedicated directory to host your packaged charts:

$ mkdir nginx-chart-files
$ cp nginx-chart-0.1.0.tgz nginx-chart-0.1.0.tgz.prov nginx-chart-files/

πŸ“ Step 2: Generate index.yaml for the Chart Repository

Use the helm repo index command to generate an index.yaml file, which acts as the index for your Helm chart repository. Specify the base URL where the charts will be hosted.

$ helm repo index nginx-chart-files/ --url https://example.com/charts

After this, you’ll see the following inside the nginx-chart-files folder:

$ ls nginx-chart-files
index.yaml  nginx-chart-0.1.0.tgz  nginx-chart-0.1.0.tgz.prov

This index.yaml file will contain metadata for your chart, such as:

apiVersion: v1
entries:
  nginx-chart:
  - apiVersion: v2
    appVersion: 1.16.0
    created: "2021-12-01T15:29:35.073405539Z"
    description: A Helm chart for Kubernetes
    digest: 2c83c29dc4c56d20c45c3de8eff521fbfb6ef6c0b66854a6f4b5539bebcff879
    maintainers:
    - email: john@example.com
      name: john smith
    name: nginx-chart
    type: application
    urls:
    - https://charts.bitnami.com/bitnami/nginx-chart-0.1.0.tgz
    version: 0.1.0
generated: "2021-12-01T15:29:35.047718855Z"

Note: The urls field must point to the downloadable path for your .tgz chart file.


🌐 Step 3: Host the Charts

Upload the contents of the nginx-chart-files folder (including index.yaml, .tgz, and .prov) to a public URL, e.g., a cloud storage bucket or a web server like:

https://example-charts.storage.googleapis.com

βž• Step 4: Add the Chart Repository to Helm

Add the newly hosted chart repository to your local Helm environment:

$ helm repo add our-cool-charts https://example-charts.storage.googleapis.com

List the repositories to confirm it was added:

$ helm repo list
NAME              URL
bitnami           https://charts.bitnami.com/bitnami
our-cool-charts   https://example-charts.storage.googleapis.com

πŸš€ Step 5: Install the Chart from the Custom Repo

Now, you can install your chart just like you would from any official repository:

$ helm install my-new-release our-cool-charts/nginx-chart

This command will fetch the nginx-chart from your custom repository and deploy it as a new release named my-new-release.

2
Subscribe to my newsletter

Read articles from Arindam Baidya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Arindam Baidya
Arindam Baidya