Learning Kubernetes - Episode 38 - Introduction and Explanation of Helm Charts

Learning Kubernetes - Episode 38 - Introduction and Explanation of Helm Charts

In this episode, we'll discuss Helm Charts for packaging and deploying Kubernetes applications. We'll learn how to create, customize, and deploy Helm charts, manage dependencies, and best practices for Helm.

Arman Dwi Pangestu
Arman Dwi PangestuApril 13, 2026
0 views
6 min read

Introduction

Note

If you want to read the previous episode, you can click the Episode 37 thumbnail below

Episode 37Episode 37

In the previous episode, we explored Pod Security Context, which controls security settings for Pods and containers. Now we'll dive into Helm Charts, which simplify packaging and deploying Kubernetes applications.

Note: Here I'll be using a Kubernetes Cluster installed through K3s.

Helm is the package manager for Kubernetes. It allows you to define, install, and upgrade complex Kubernetes applications using charts. Think of Helm charts like Docker images for Kubernetes - they package everything needed to run an application. Instead of managing dozens of YAML files manually, Helm lets you template and version your deployments.

Understanding Helm Charts

A Helm chart is a collection of files that describe a set of Kubernetes resources. It's essentially a templated package that can be customized and deployed to a cluster.

Why Helm Charts Matter

1. Simplify Deployment

Package complex applications into a single deployable unit.

2. Reusability

Share charts across teams and organizations.

3. Templating

Use variables and logic to customize deployments.

4. Versioning

Track and manage different versions of applications.

5. Dependency Management

Manage dependencies between applications.

6. Rollback Capability

Easily rollback to previous versions.

Helm Chart Structure

A typical Helm chart has this structure:

plaintext
my-chart/
├── Chart.yaml
├── values.yaml
├── values-prod.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── configmap.yaml
│   ├── ingress.yaml
│   └── _helpers.tpl
├── charts/
│   └── dependency-chart/
└── README.md

Chart.yaml

Metadata about the chart:

KubernetesChart.yaml
apiVersion: v2
name: my-app
description: A Helm chart for my application
type: application
version: 1.0.0
appVersion: "1.0"
keywords:
  - app
  - kubernetes
maintainers:
  - name: Your Name
    email: your@email.com

values.yaml

Default configuration values:

Kubernetesvalues.yaml
replicaCount: 3
 
image:
  repository: myapp
  tag: "1.0"
  pullPolicy: IfNotPresent
 
service:
  type: ClusterIP
  port: 80
  targetPort: 8080
 
ingress:
  enabled: true
  className: nginx
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix
 
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

Templates

Kubernetes manifests with template variables:

Kubernetestemplates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "my-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-app.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - name: http
          containerPort: {{ .Values.service.targetPort }}
          protocol: TCP
        resources:
          {{- toYaml .Values.resources | nindent 12 }}

Creating a Helm Chart

Initialize a Chart

Kubernetesbash
helm create my-app

This creates a basic chart structure with example files.

Chart Validation

Kubernetesbash
helm lint my-app

Validates the chart for errors.

Template Rendering

Kubernetesbash
helm template my-app ./my-app

Shows the rendered YAML without installing.

Dry Run

Kubernetesbash
helm install my-release ./my-app --dry-run --debug

Simulates the installation without actually deploying.

Installing Helm Charts

Basic Installation

Kubernetesbash
helm install my-release ./my-app

Installs the chart with default values.

Custom Values

Kubernetesbash
helm install my-release ./my-app -f values-prod.yaml

Installs with custom values from a file.

Override Values

Kubernetesbash
helm install my-release ./my-app \
  --set replicaCount=5 \
  --set image.tag=2.0

Overrides specific values from the command line.

Namespace

Kubernetesbash
helm install my-release ./my-app -n production --create-namespace

Installs in a specific namespace.

Managing Helm Releases

List Releases

Kubernetesbash
helm list
helm list -n production

Shows all installed releases.

Get Release Info

Kubernetesbash
helm status my-release
helm get values my-release
helm get manifest my-release

Shows release status and configuration.

Upgrade Release

Kubernetesbash
helm upgrade my-release ./my-app

Upgrades to a new version.

Rollback Release

Kubernetesbash
helm rollback my-release 1

Rolls back to a previous revision.

Uninstall Release

Kubernetesbash
helm uninstall my-release

Removes the release from the cluster.

Helm Repositories

Add Repository

Kubernetesbash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

Adds a Helm repository and updates the index.

Search Charts

Kubernetesbash
helm search repo bitnami
helm search repo bitnami/nginx

Searches for charts in repositories.

Install from Repository

Kubernetesbash
helm install my-nginx bitnami/nginx

Installs a chart from a repository.

Template Functions

Built-in Functions

KubernetesTemplate Functions
# String functions
{{ .Values.name | upper }}
{{ .Values.name | lower }}
{{ .Values.name | quote }}
 
# Conditional
{{ if .Values.enabled }}
  enabled: true
{{ end }}
 
# Loops
{{ range .Values.items }}
  - {{ . }}
{{ end }}
 
# Default values
{{ .Values.name | default "default-name" }}
 
# Include templates
{{ include "my-app.labels" . }}

Helper Templates

Kubernetestemplates/_helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "my-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
 
{{/*
Create a default fully qualified app name.
*/}}
{{- define "my-app.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
 
{{/*
Common labels
*/}}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ include "my-app.chart" . }}
{{ include "my-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
 
{{/*
Selector labels
*/}}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

Chart Dependencies

Chart.yaml with Dependencies

KubernetesChart.yaml with Dependencies
apiVersion: v2
name: my-app
version: 1.0.0
dependencies:
  - name: postgresql
    version: "12.0.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
  - name: redis
    version: "17.0.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled

Update Dependencies

Kubernetesbash
helm dependency update ./my-app

Downloads and updates chart dependencies.

values.yaml with Dependencies

Kubernetesvalues.yaml with Dependencies
postgresql:
  enabled: true
  auth:
    username: myapp
    password: secret
    database: myapp_db
 
redis:
  enabled: true
  auth:
    enabled: false

Practical Examples

Simple Web Application Chart

Kubernetestemplates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "web-app.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ include "web-app.name" . }}
  template:
    metadata:
      labels:
        app: {{ include "web-app.name" . }}
    spec:
      containers:
      - name: web
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        ports:
        - containerPort: {{ .Values.service.port }}
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: {{ include "web-app.fullname" . }}-secret
              key: database-url
        resources:
          {{- toYaml .Values.resources | nindent 12 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ include "web-app.fullname" . }}
spec:
  type: {{ .Values.service.type }}
  ports:
  - port: {{ .Values.service.port }}
    targetPort: {{ .Values.service.port }}
  selector:
    app: {{ include "web-app.name" . }}

Conditional Resources

KubernetesConditional Ingress
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "web-app.fullname" . }}
spec:
  ingressClassName: {{ .Values.ingress.className }}
  rules:
  {{- range .Values.ingress.hosts }}
  - host: {{ .host | quote }}
    http:
      paths:
      {{- range .paths }}
      - path: {{ .path }}
        pathType: {{ .pathType }}
        backend:
          service:
            name: {{ include "web-app.fullname" . }}
            port:
              number: {{ $.Values.service.port }}
      {{- end }}
  {{- end }}
{{- end }}

Common Mistakes and Pitfalls

Mistake 1: Hardcoding Values

Problem: Chart is not reusable.

KubernetesMistake: Hardcoded Values
# DON'T DO THIS - Hardcoded values
image: myapp:1.0
replicas: 3

Solution: Use template variables:

KubernetesCorrect: Template Variables
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
replicas: {{ .Values.replicaCount }}

Mistake 2: Missing Default Values

Problem: Chart fails if values are not provided.

KubernetesMistake: No Defaults
# DON'T DO THIS - No default values
image: "{{ .Values.image }}"

Solution: Provide defaults:

KubernetesCorrect: With Defaults
image: "{{ .Values.image | default "nginx:latest" }}"

Mistake 3: Inconsistent Naming

Problem: Resources have inconsistent names.

KubernetesMistake: Inconsistent Names
# DON'T DO THIS - Inconsistent naming
metadata:
  name: my-app
---
metadata:
  name: myapp

Solution: Use helper templates:

KubernetesCorrect: Consistent Names
metadata:
  name: {{ include "my-app.fullname" . }}

Mistake 4: Not Testing Charts

Problem: Chart has errors when deployed.

Solution: Always test before deploying:

Kubernetesbash
helm lint ./my-app
helm template ./my-app
helm install --dry-run --debug ./my-app

Mistake 5: Poor Documentation

Problem: Users don't know how to use the chart.

Solution: Document values and usage:

KubernetesREADME.md
# My App Helm Chart
 
## Installation
 
```bash
helm install my-release ./my-app
```
 
## Configuration
 
| Parameter | Description | Default |
|-----------|-------------|---------|
| replicaCount | Number of replicas | 3 |
| image.repository | Image repository | myapp |
| image.tag | Image tag | 1.0 |

Best Practices

1. Use Semantic Versioning

Kubernetesyaml
version: 1.2.3
appVersion: "2.0.1"

2. Provide Comprehensive values.yaml

Document all available values with comments:

Kubernetesyaml
# Number of replicas
replicaCount: 3
 
# Image configuration
image:
  # Image repository
  repository: myapp
  # Image tag
  tag: "1.0"
  # Image pull policy
  pullPolicy: IfNotPresent

3. Use Helper Templates

Create reusable template helpers:

Kubernetesyaml
{{- define "my-app.fullname" -}}
{{- .Release.Name }}-{{ .Chart.Name }}
{{- end }}

4. Validate Charts

Always validate before deploying:

Kubernetesbash
helm lint ./my-app
helm template ./my-app

5. Use Namespaces

Deploy to specific namespaces:

Kubernetesbash
helm install my-release ./my-app -n production --create-namespace

6. Version Your Charts

Track chart versions in version control:

Kubernetesbash
git tag chart-v1.0.0

7. Document Values

Create a comprehensive README:

Kubernetesmarkdown
# Chart Documentation
 
## Values
 
- `replicaCount`: Number of replicas (default: 3)
- `image.repository`: Image repository (default: myapp)
- `image.tag`: Image tag (default: 1.0)

Helm Hooks

Execute actions at specific points in the release lifecycle:

KubernetesPre-Install Hook
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-app.fullname" . }}-pre-install
  annotations:
    "helm.sh/hook": pre-install
    "helm.sh/hook-weight": "-5"
spec:
  template:
    spec:
      containers:
      - name: pre-install
        image: busybox
        command: ['sh', '-c', 'echo "Pre-install hook"']
      restartPolicy: Never

Hook Types

  • pre-install - Before install
  • post-install - After install
  • pre-upgrade - Before upgrade
  • post-upgrade - After upgrade
  • pre-delete - Before delete
  • post-delete - After delete
  • pre-rollback - Before rollback
  • post-rollback - After rollback

Conclusion

In episode 38, we've explored Helm Charts in Kubernetes in depth. We've learned how to create, customize, and deploy Helm charts, manage dependencies, and follow best practices.

Key takeaways:

  • Helm Charts package Kubernetes applications
  • Chart.yaml - Chart metadata
  • values.yaml - Default configuration
  • templates/ - Kubernetes manifests with variables
  • helm create - Initialize a new chart
  • helm install - Deploy a chart
  • helm upgrade - Update a release
  • helm rollback - Revert to previous version
  • Template functions - Customize deployments
  • Dependencies - Manage chart dependencies
  • Repositories - Share and discover charts
  • Hooks - Execute actions at lifecycle events
  • Always validate - Test charts before deploying
  • Document values - Help users understand configuration
  • Use semantic versioning - Track chart versions

Helm charts simplify Kubernetes deployments and make applications more reusable and maintainable.

Note

If you want to continue to the next episode, you can click the Episode 39 thumbnail below

Episode 39Episode 39

Related Posts