Helm — Package Manager for Kubernetes
Helm is the Kubernetes equivalent of
apt,brew, ormaven— it packages, versions, and deploys Kubernetes applications.
Why Helm?
GitOps ArgoCD Sync Pipeline & Kubernetes Operator Reconciler
GITOPS RECONCILIATION STAGES:
1. Developer Git Commit
Actor: Developer / Git Repo
2. ArgoCD Webhook Event
Actor: Git Repository
3. Drift Detection (OutOfSync)
Actor: ArgoCD Controller
4. Automated Reconciliation
Actor: ArgoCD Application Engine
5. Progressive Canary Rollout
Actor: Argo Rollouts Engine
6. Cluster State Synced
Actor: K8s Cluster State
ArgoCD Reconciliation Engine Stage
1. Developer Git Commit
Developer commits updated Kubernetes manifests / Helm values to GitHub. Git is the single source of truth.
💡 GitOps Key Principle: Pull-based agents running inside the cluster eliminate the security vulnerability of exposing cluster SSH/kubeconfig credentials to external CI runners.
Without Helm, deploying an application means maintaining dozens of individual YAML files.
With Helm, it's a versioned, parameterised chart with a single command to install.
# Without Helm: apply 8+ files, manage env differences manually
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/pvc.yaml
kubectl apply -f k8s/hpa.yaml
# With Helm: one command
helm install my-api ./my-api-chart -f values.production.yaml
Core Concepts
| Term | Definition |
|---|---|
| Chart | Package of K8s YAML templates + default values |
| Release | Installed instance of a chart |
| Values | Configuration that parameterises the chart |
| Repository | Remote store of charts (like Maven Central) |
| Template | YAML file with Go template variables |
| Revision | Numbered history of a release's changes |
Helm Chart Structure
my-api/ ← Chart directory (name = chart name)
├── Chart.yaml ← Chart metadata
├── values.yaml ← Default configuration values
├── values.production.yaml ← Production overrides (not standard, by convention)
├── charts/ ← Sub-charts (dependencies)
├── templates/ ← K8s YAML templates
│ ├── _helpers.tpl ← Reusable template helpers (partial templates)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ └── NOTES.txt ← Displayed after install
└── .helmignore ← Files to exclude from packaging
Chart.yaml
apiVersion: v2 # v2 for Helm 3
name: my-api
description: Spring Boot REST API service
type: application # application | library
version: 1.2.0 # Chart version (semver)
appVersion: "2.5.0" # App version (informational)
keywords:
- api
- spring-boot
home: https://github.com/myorg/my-api
maintainers:
- name: Backend Team
# Chart dependencies (sub-charts)
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # Only include if postgresql.enabled=true
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
values.yaml (Default Values)
# values.yaml — these are the defaults
replicaCount: 2
image:
repository: myapp
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: false
className: nginx
host: api.example.com
tls: false
tlsSecretName: ""
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
config:
springProfile: default
logLevel: INFO
dbHost: localhost
dbPort: 5432
dbName: mydb
postgresql:
enabled: true # Install sub-chart
auth:
database: mydb
username: myapp
password: ""
redis:
enabled: false
nodeSelector: {}
tolerations: []
affinity: {}
Templates
Go template syntax renders values into YAML.
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-api.fullname" . }} # From _helpers.tpl
namespace: {{ .Release.Namespace }}
labels:
{{- include "my-api.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-api.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-api.selectorLabels" . | nindent 8 }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
# ↑ Forces Pod restart when ConfigMap changes
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.targetPort }}
env:
- name: SPRING_PROFILES_ACTIVE
value: {{ .Values.config.springProfile | quote }}
- name: DB_HOST
value: {{ .Values.config.dbHost | quote }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "my-api.fullname" . }}-secret
key: db-password
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
_helpers.tpl (Reusable Partials)
{{/*
Expand the name of the chart.
*/}}
{{- define "my-api.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "my-api.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "my-api.labels" -}}
helm.sh/chart: {{ include "my-api.chart" . }}
{{ include "my-api.selectorLabels" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "my-api.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-api.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
Helm CLI Commands
# ─── Repository Management ────────────────────────────────────────
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo update # Fetch latest chart versions
helm repo list
helm repo remove bitnami
# ─── Search ───────────────────────────────────────────────────────
helm search repo postgres # Search added repos
helm search hub nginx # Search Artifact Hub (all public charts)
helm search repo bitnami/postgresql --versions # List all versions
# ─── Install ──────────────────────────────────────────────────────
helm install my-api ./my-api # From local chart
helm install my-api bitnami/postgresql # From repo
helm install my-api ./my-api \
-f values.production.yaml # Custom values file
helm install my-api ./my-api \
--set image.tag=1.3.0 \ # Inline value override
--set replicaCount=3
helm install my-api ./my-api \
--namespace production \
--create-namespace \ # Create namespace if needed
--wait # Wait for pods to be ready
# Dry run (render templates without installing)
helm install my-api ./my-api --dry-run --debug
# ─── Upgrade ──────────────────────────────────────────────────────
helm upgrade my-api ./my-api
helm upgrade my-api ./my-api -f values.production.yaml
helm upgrade my-api ./my-api --set image.tag=1.4.0
helm upgrade my-api ./my-api --reuse-values # Keep existing values, only change specified
# Install if not exists, upgrade if exists
helm upgrade --install my-api ./my-api \
-f values.production.yaml \
--namespace production \
--create-namespace
# ─── Rollback ─────────────────────────────────────────────────────
helm rollback my-api # Roll back to previous release
helm rollback my-api 2 # Roll back to revision 2
# ─── Status / History ─────────────────────────────────────────────
helm list # All releases in current namespace
helm list -n production
helm list -A # All namespaces
helm status my-api # Release status
helm history my-api # Release revision history
# ─── Uninstall ────────────────────────────────────────────────────
helm uninstall my-api
helm uninstall my-api -n production
helm uninstall my-api --keep-history # Uninstall but keep history
# ─── Template Debugging ───────────────────────────────────────────
helm template my-api ./my-api # Render templates to stdout
helm template my-api ./my-api -f values.production.yaml
helm template my-api ./my-api --debug # Verbose render with parsed values
helm lint ./my-api # Lint chart for errors
# ─── Inspect Chart ────────────────────────────────────────────────
helm show chart bitnami/postgresql # Chart.yaml
helm show values bitnami/postgresql # Default values.yaml
helm show all bitnami/postgresql # Everything
# ─── Package and Push ─────────────────────────────────────────────
helm package ./my-api # Creates my-api-1.2.0.tgz
helm push my-api-1.2.0.tgz oci://registry.example.com/charts
Values Override Patterns
# Multiple values files (merged in order, later files win)
helm upgrade --install my-api ./my-api \
-f values.yaml \ # Base defaults
-f values.production.yaml \ # Production overrides
-f values.secrets.yaml # Secret values (not committed to git)
# Inline override (highest precedence)
helm upgrade my-api ./my-api \
--set image.tag=$CI_COMMIT_SHA \
--set replicaCount=5
Dependencies (Sub-Charts)
# After adding dependencies to Chart.yaml:
helm dependency update ./my-api # Downloads sub-charts to charts/
# Lock dependencies to exact versions
helm dependency build ./my-api # Generates Chart.lock
# Install with embedded dependencies
helm install my-api ./my-api # Deploys app + postgres + redis
Creating a Chart from Scratch
# Scaffold a new chart
helm create my-api
# Creates standard directory structure with example templates
# Edit Chart.yaml, values.yaml, templates/
# Delete templates you don't need
# Test rendering
helm template my-api ./my-api
# Lint
helm lint ./my-api
# Install
helm install my-api ./my-api --dry-run
helm install my-api ./my-api
Interview Questions
- What is Helm and what problem does it solve?
- What is the difference between a Helm chart and a Helm release?
- What is
values.yamlused for? - How do you override chart values at install time?
- How do you roll back a Helm release?
- What does
helm upgrade --installdo? - What is
helm templateused for? - What is a chart dependency?
- How does Helm manage release history?
- What is
helm lintand why should you run it in CI?
