Getting Started with Helm for Kubernetes Package Management

Helm Overview

Helm serves as the package manager for Kubernetes, analogous to apt or yum in Linux environments. It streamlines the deployment of applications within a Kubernetes cluster by enabling you to define, install, and upgrade complex applications through reusable packages called Charts.

Core Concepts

Chart

A Chart represents a self-contained package containing all necessary resources to run an application on Kubernetes. This includes Docker images, Kubernetes manifests, metadata, and service definitions. Charts function similarly to Homebrew formulas, APT packages (dpkg), or RPM files in traditional Linux package management.

Release

A Release denotes a specific instance of a Chart deployed to a Kubernetes cluster. The same Chart can be installed multiple times within a cluster, with each installation generating a distinct Release. For instance, if you need two separate MySQL databases, you would install the MySQL Chart twice, each creating its own Release with a unique name.

Repository

A Repository serves as a storage location where Charts are published and distributed from. You can configure multiple repositories and search across them to find available packages for installation.

Helm Architecture

Chart Installation Workflow

  1. Helm parses the Chart structure from a directory or compressed archive
  2. Helm transmits the Chart structure and Values to Tiller via gRPC
  3. Tiller constructs a Release based on the provided Chart and Values
  4. Tiller submits the Release to Kubernetes for execution

Chart Update Workflow

  1. Helm parses the Chart structure from a directory or compressed archive
  2. Helm sends the Release name, updated Chart structure, and Values to Tiller
  3. Tiller generates a new Release and updates the Release history for the specified name
  4. Tiller submits the updated Release to Kubernetes for execution

Installation

Helm consists of two primary components: the client CLI and the Tiller server component. Tiller runs as a pod within your Kubernetes cluster and handles the actual deployment operations.

If you're using Alibaba Cloud Container Service for Kubernetes, Tiller may already be pre-installed. In such cases, you only need to install the Helm client.

Download the appropriate release for your platform:

After downloading, extract the binary to a directory of your choice and add that directory to your system PATH.

Helm requires access to your Kubernetes cluster configuration. This can be achieved through the standard kubectl configuration (~/.kube/config) or by specifying a custom kubeconfig file with the --kubeconfig flag.

# Deploying an application named webapp using a Chart in ./chart directory
# with a custom kubeconfig file
/path/to/helm --kubeconfig /path/to/kube.conf install webapp ./chart

Server Installation

Initialize the Helm client and install Tiller in your cluster:

helm init

Chart repositories are optional since many teams bundle Charts alongside they source code in Git repositories. You can start a local repository server using helm serve if needed.

Practical Usage

This section demonstrates Helm's capabilities by walking through the creation and deployment of a web application Chart. Docker image preparation is assumed to be completed beforehand.

Creating a Chart

Generate a new Chart using the helm create command:

helm create webapp

The generated structure includes:

webapp/
├── charts/                  # Directory for dependency Charts
├── Chart.yaml               # Chart metadata (name, version)
├── templates/               # Kubernetes manifest templates
│   ├── deployment.yaml
│   ├── _helpers.tpl         # Shared template definitions (underscore prefix)
│   ├── ingress.yaml
│   ├── NOTES.txt            # Installation instructions displayed post-deploy
│   └── service.yaml
└── values.yaml              # Default parameter values for templates

Kubernetes Manifest Templates

deployment.yaml

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
        - name: webapp
          image: myregistry/webapp:2.1.0
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  selector:
    app: webapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: webapp-ingress
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            backend:
              serviceName: webapp-service
              servicePort: 80

Converting to Template Files

Extracting parameters into template variables enables reuse across multiple environments. Templates use Go's text template syntax with double braces {{ }} for dynamic content injection.

deployment.yaml Template

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

service.yaml Template

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-service
spec:
  selector:
    app: {{ .Release.Name }}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

ingress.yaml Template

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ .Release.Name }}-ingress
spec:
  rules:
    - host: {{ .Values.ingress.host }}
      http:
        paths:
          - path: /
            backend:
              serviceName: {{ .Release.Name }}-service
              servicePort: 80

values.yaml

ingress:
  host: app.example.com

image:
  repository: myregistry/webapp
  tag: 2.1.0

replicas: 1

Installing and Upgrading Releases

Install a Chart:

helm install ./webapp

Install with custom parameters:

helm install \
  --set replicas=2 \
  --set ingress.host=staging.example.com \
  ./webapp

Upgrade an existing Release:

helm upgrade webapp ./webapp

Upgrade with custom parameters:

helm upgrade \
  --set replicas=3 \
  --set ingress.host=production.example.com \
  webapp ./webapp

Atomic install-or-upgrade:

helm upgrade -i \
  --set replicas=2 \
  --set ingress.host=app.example.com \
  webapp ./webapp

The -i (install) flag creates the Release if it doesn't exist, or updates it if it does.

Template Syntax Reference

Expressions

Template expressions are enclosed in double braces. Whitespace trimming variants:

  • {{ expression }} - standard output
  • {{- expression }} - trim preceding whitespace
  • {{ expression -}} - trim trailing whitespace
  • {{- expression -}} - trim both

Variables and Scope

The dot (.) represents the root scope for accessing objects. Helm provides built-in global objects:

Values Object

Access values.yaml parameters:

{{ .Values.replicas }}
{{ .Values.image.repository }}

Release Object

Property Description
.Release.Name Release identifier
.Release.Time Installation timestamp
.Release.Namespace Target namespace
.Release.Revision Incrementing version number
.Release.IsUpgrade True during upgrade operations
.Release.IsInstall True during installation operations

User-Defined Variables

Assign variables with :=:

{{- $releaseName := .Release.Name -}}
{{ $releaseName }}

Functions and Pipelines

Function Call:

{{ upper .Values.environment }}

Pipeline Operations:

{{ .Values.environment | upper }}
{{ .Values.environment | upper | quote }}
{{ .Values.environment | default "development" }}
{{ .Values.tag | repeat 3 }}
{{ .Values.description | nindent 2 }}

Comparison Operators

All operators function as template functions:

Operator Function Description
== eq Equal
!= ne Not equal
< lt Less than
> gt Greater than
&& and Logical AND
|| or Logical OR
! not Logical NOT
{{ if and .Values.enabled (eq .Values.environment "production") }}
enabled: true
{{ end }}

Control Structures

Conditional Statements

{{ if eq .Values.drinks "coffee" }}
caffeinated: true
{{ else }}
caffeinated: false
{{ end }}

Scope Modification with with

The with statement changes the current scope:

{{- with .Values.configuration }}
maxConnections: {{ .max }}
timeout: {{ .timeout }}
{{- end }}

Access parent scope within with:

{{- $rootScope := . -}}
{{- with .Values.configuration }}
application: {{ $rootScope.Release.Name }}
maxConnections: {{ .max }}
{{- end }}

Looping with range

Iterating over maps:

{{- range $key, $value := .Values.configuration }}
{{ $key }}: {{ $value | quote }}
{{- end }}

Iterating over arrays:

{{- range .Values.servers }}
- {{ .name }}: {{ .address | quote }}
{{- end }}

Named Templates

Define reusable templates in files prefixed with underscore. These files (like _helpers.tpl) are not rendered directly to Kubernetes.

Definition:

{{- define "labels" -}}
app: {{ .Chart.Name }}
version: "{{ .Chart.Version }}"
{{- end -}}

Usage:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
  labels:
    {{ include "labels" . | nindent 4 }}
data:
  config: "value"

Debugging Templates

Use --dry-run combined with --debug to preview rendered manifests without submitting them to Kubernetes:

helm upgrade \
  --debug \
  --dry-run \
  -i \
  --set replicas=2 \
  --set ingress.host=app.example.com \
  webapp ./webapp

This renders the templates with your provided values and displays the output, allowing you to verify correctness before actual deployment.

Tags: kubernetes Helm package-management Chart k8s

Posted on Sat, 15 Aug 2026 16:16:55 +0000 by stevefriedman71