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.

Note
If you want to read the previous episode, you can click the Episode 37 thumbnail below
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.
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.
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.
A typical Helm chart has this structure:
my-chart/
├── Chart.yaml
├── values.yaml
├── values-prod.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── ingress.yaml
│ └── _helpers.tpl
├── charts/
│ └── dependency-chart/
└── README.mdMetadata about the chart:
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.comDefault configuration values:
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: 256MiKubernetes manifests with template variables:
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 }}helm create my-appThis creates a basic chart structure with example files.
helm lint my-appValidates the chart for errors.
helm template my-app ./my-appShows the rendered YAML without installing.
helm install my-release ./my-app --dry-run --debugSimulates the installation without actually deploying.
helm install my-release ./my-appInstalls the chart with default values.
helm install my-release ./my-app -f values-prod.yamlInstalls with custom values from a file.
helm install my-release ./my-app \
--set replicaCount=5 \
--set image.tag=2.0Overrides specific values from the command line.
helm install my-release ./my-app -n production --create-namespaceInstalls in a specific namespace.
helm list
helm list -n productionShows all installed releases.
helm status my-release
helm get values my-release
helm get manifest my-releaseShows release status and configuration.
helm upgrade my-release ./my-appUpgrades to a new version.
helm rollback my-release 1Rolls back to a previous revision.
helm uninstall my-releaseRemoves the release from the cluster.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo updateAdds a Helm repository and updates the index.
helm search repo bitnami
helm search repo bitnami/nginxSearches for charts in repositories.
helm install my-nginx bitnami/nginxInstalls a chart from a repository.
# 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" . }}{{/*
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 }}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.enabledhelm dependency update ./my-appDownloads and updates chart dependencies.
postgresql:
enabled: true
auth:
username: myapp
password: secret
database: myapp_db
redis:
enabled: true
auth:
enabled: falseapiVersion: 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" . }}{{- 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 }}Problem: Chart is not reusable.
# DON'T DO THIS - Hardcoded values
image: myapp:1.0
replicas: 3Solution: Use template variables:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
replicas: {{ .Values.replicaCount }}Problem: Chart fails if values are not provided.
# DON'T DO THIS - No default values
image: "{{ .Values.image }}"Solution: Provide defaults:
image: "{{ .Values.image | default "nginx:latest" }}"Problem: Resources have inconsistent names.
# DON'T DO THIS - Inconsistent naming
metadata:
name: my-app
---
metadata:
name: myappSolution: Use helper templates:
metadata:
name: {{ include "my-app.fullname" . }}Problem: Chart has errors when deployed.
Solution: Always test before deploying:
helm lint ./my-app
helm template ./my-app
helm install --dry-run --debug ./my-appProblem: Users don't know how to use the chart.
Solution: Document values and usage:
# 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 |version: 1.2.3
appVersion: "2.0.1"Document all available values with comments:
# Number of replicas
replicaCount: 3
# Image configuration
image:
# Image repository
repository: myapp
# Image tag
tag: "1.0"
# Image pull policy
pullPolicy: IfNotPresentCreate reusable template helpers:
{{- define "my-app.fullname" -}}
{{- .Release.Name }}-{{ .Chart.Name }}
{{- end }}Always validate before deploying:
helm lint ./my-app
helm template ./my-appDeploy to specific namespaces:
helm install my-release ./my-app -n production --create-namespaceTrack chart versions in version control:
git tag chart-v1.0.0Create a comprehensive README:
# Chart Documentation
## Values
- `replicaCount`: Number of replicas (default: 3)
- `image.repository`: Image repository (default: myapp)
- `image.tag`: Image tag (default: 1.0)Execute actions at specific points in the release lifecycle:
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: Neverpre-install - Before installpost-install - After installpre-upgrade - Before upgradepost-upgrade - After upgradepre-delete - Before deletepost-delete - After deletepre-rollback - Before rollbackpost-rollback - After rollbackIn 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 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