Helm
Kubernetes package manager for deploying and managing applications as charts.
Helm
Kubernetes package manager for deploying and managing applications as charts.
Overview
Helm is the de facto package manager for Kubernetes, enabling you to define, install, and upgrade complex Kubernetes applications. It uses a packaging format called charts, which are collections of files that describe a related set of Kubernetes resources. Helm simplifies deployment workflows, manages application dependencies, and provides templating capabilities for configuration management.
This sheet targets Helm v4 (released November 2025); everything shown is also valid on Helm 3.8+ unless noted.
flowchart LR
subgraph "Helm Architecture"
A[Helm CLI] --> B[Chart Repository]
A --> C[Kubernetes API]
B --> D[Chart Package]
D --> E[Templates]
D --> F[Values]
D --> G[Chart.yaml]
E --> H[Rendered Manifests]
F --> H
H --> C
C --> I[Release]
end
Chart Structure
A Helm chart is a collection of files organised in a specific directory structure that describes a Kubernetes application.
Key Concepts
- Chart: A package containing all resource definitions needed to run an application
- Release: An instance of a chart running in a Kubernetes cluster
- Repository: A location where charts can be collected and shared
- Values: Configuration that can be merged into the template
graph TD
subgraph "Chart Directory Structure"
A[mychart/] --> B[Chart.yaml]
A --> C[values.yaml]
A --> D[charts/]
A --> E[templates/]
A --> F[.helmignore]
E --> G[deployment.yaml]
E --> H[service.yaml]
E --> I[_helpers.tpl]
E --> J[NOTES.txt]
E --> K[tests/]
end
Chart.yaml
The main metadata file for the chart.
apiVersion: v2
name: myapp
description: A Helm chart for my application
type: application
version: 1.0.0
appVersion: "2.1.0"
keywords:
- web
- application
home: https://example.com
sources:
- https://github.com/example/myapp
maintainers:
- name: John Smith
email: john@example.com
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
values.yaml
Default configuration values for the chart.
# Number of replicas
replicaCount: 3
image:
repository: nginx
pullPolicy: IfNotPresent
tag: "1.21"
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilisationPercentage: 80
Templates Directory
Contains template files that generate Kubernetes manifests.
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.port }}
Examples
Create a new chart:
# Create chart with default structure
helm create myapp
# View created structure
tree myapp/
Package a chart:
# Package chart into .tgz archive
helm package myapp/
# Package with specific version
helm package myapp/ --version 1.2.0
# Package to specific directory
helm package myapp/ --destination ./releases/
Installing and Upgrading Releases
Helm manages the lifecycle of chart installations through releases.
Key Concepts
stateDiagram-v2
[*] --> Pending: helm install
Pending --> Deployed: Success
Pending --> Failed: Error
Deployed --> Superseded: helm upgrade
Deployed --> Uninstalled: helm uninstall
Superseded --> Deployed: New revision
Failed --> Deployed: helm upgrade --install
Uninstalled --> [*]
- Install: Create a new release from a chart
- Upgrade: Update an existing release with new values or chart version
- Rollback: Revert to a previous release revision
- Uninstall: Remove a release from the cluster
Common Commands/Patterns
# Install a chart
helm install myrelease ./mychart
# Install from repository
helm install myrelease bitnami/nginx
# Install with custom values file
helm install myrelease ./mychart -f custom-values.yaml
# Install with inline values
helm install myrelease ./mychart --set replicaCount=5
# Install in specific namespace (create if not exists)
helm install myrelease ./mychart -n production --create-namespace
# Dry run to see generated manifests
helm install myrelease ./mychart --dry-run --debug
# Wait for resources to be ready
helm install myrelease ./mychart --wait --timeout 5m
# Install or upgrade (idempotent)
helm upgrade --install myrelease ./mychart
Upgrade Operations
# Upgrade with new values
helm upgrade myrelease ./mychart --set replicaCount=10
# Upgrade with values file
helm upgrade myrelease ./mychart -f production-values.yaml
# Upgrade and reuse previous values
helm upgrade myrelease ./mychart --reuse-values
# Upgrade and reset values to defaults
helm upgrade myrelease ./mychart --reset-values
# Atomic upgrade (rollback on failure)
helm upgrade myrelease ./mychart --atomic
# Force resource updates
helm upgrade myrelease ./mychart --force
Rollback and Uninstall
# View release history
helm history myrelease
# Rollback to previous revision
helm rollback myrelease
# Rollback to specific revision
helm rollback myrelease 2
# Uninstall a release
helm uninstall myrelease
# Uninstall but keep history
helm uninstall myrelease --keep-history
# Uninstall from specific namespace
helm uninstall myrelease -n production
Examples
Complete deployment workflow:
# Add repository
helm repo add bitnami https://charts.bitnami.com/bitnami
# Search for chart
helm search repo nginx
# Show chart values
helm show values bitnami/nginx > nginx-values.yaml
# Customise values
cat <<EOF > my-nginx-values.yaml
replicaCount: 3
service:
type: LoadBalancer
EOF
# Install with custom values
helm install my-nginx bitnami/nginx -f my-nginx-values.yaml -n web --create-namespace
# Check status
helm status my-nginx -n web
# Upgrade
helm upgrade my-nginx bitnami/nginx -f my-nginx-values.yaml --set replicaCount=5 -n web
# View history
helm history my-nginx -n web
Template Functions and Pipelines
Helm uses Go templates with Sprig functions and custom Helm functions for dynamic manifest generation.
Key Concepts
flowchart LR
A[values.yaml] --> B[Template Engine]
C[Built-in Objects] --> B
D[Sprig Functions] --> B
E[Template Files] --> B
B --> F[Rendered YAML]
- Actions: Template directives enclosed in
{{ }} - Pipelines: Chain of commands separated by
| - Built-in Objects:
.Values,.Chart,.Release,.Capabilities,.Files - Sprig Functions: String, math, date, and other utility functions
Built-in Objects
# .Release - Release information
{{ .Release.Name }} # Release name
{{ .Release.Namespace }} # Release namespace
{{ .Release.Revision }} # Release revision number
{{ .Release.IsUpgrade }} # True if upgrade operation
{{ .Release.IsInstall }} # True if install operation
{{ .Release.Service }} # Always "Helm"
# .Chart - Chart.yaml contents
{{ .Chart.Name }} # Chart name
{{ .Chart.Version }} # Chart version
{{ .Chart.AppVersion }} # Application version
# .Values - values.yaml contents
{{ .Values.replicaCount }}
{{ .Values.image.repository }}
# .Capabilities - Kubernetes cluster capabilities
{{ .Capabilities.KubeVersion }}
{{ .Capabilities.APIVersions.Has "apps/v1" }}
# .Files - Access non-template files
{{ .Files.Get "config.ini" }}
{{ .Files.AsSecrets }}
Common Functions and Pipelines
# String functions
{{ .Values.name | upper }} # Uppercase
{{ .Values.name | lower }} # Lowercase
{{ .Values.name | title }} # Title case
{{ .Values.name | quote }} # Add quotes
{{ .Values.name | trim }} # Trim whitespace
{{ .Values.name | replace "old" "new" }} # Replace string
{{ printf "%s-%s" .Release.Name .Chart.Name }} # Format string
# Default values
{{ .Values.name | default "myapp" }} # Default if empty
{{ .Values.port | default 8080 }}
# Required values
{{ required "A name is required!" .Values.name }}
# Conditional logic
{{- if .Values.ingress.enabled }}
# Ingress configuration here
{{- end }}
{{- if and .Values.a .Values.b }}
# Both a and b are true
{{- end }}
{{- if or .Values.a .Values.b }}
# Either a or b is true
{{- end }}
# Ternary operator
{{ ternary "yes" "no" .Values.enabled }}
# Loops
{{- range .Values.hosts }}
- {{ . | quote }}
{{- end }}
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
# Indentation
{{ include "myapp.labels" . | nindent 4 }}
{{ toYaml .Values.resources | indent 2 }}
Named Templates (Helpers)
# templates/_helpers.tpl
{{/*
Create chart name and version for chart label.
*/}}
{{- define "myapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ include "myapp.chart" . }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account
*/}}
{{- define "myapp.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "myapp.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
Examples
Complex template with conditionals:
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "myapp.fullname" . }}-config
labels:
{{- include "myapp.labels" . | nindent 4 }}
data:
{{- if .Values.config }}
{{- range $key, $value := .Values.config }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
{{- if .Values.configFile }}
config.yaml: |
{{- .Values.configFile | nindent 4 }}
{{- end }}
{{- with .Files.Glob "configs/*" }}
{{- range $path, $content := . }}
{{ base $path }}: |
{{- $.Files.Get $path | nindent 4 }}
{{- end }}
{{- end }}
Template debugging:
# Render templates locally
helm template myrelease ./mychart
# Render with specific values
helm template myrelease ./mychart -f values-prod.yaml
# Show only specific template
helm template myrelease ./mychart -s templates/deployment.yaml
# Debug with verbose output
helm template myrelease ./mychart --debug
Dependencies Management
Helm charts can depend on other charts, allowing for modular and reusable components.
Key Concepts
- Dependencies: Charts that are required by your chart
- Subcharts: Dependencies stored in the
charts/directory - Conditions: Enable/disable dependencies based on values
- Tags: Group dependencies for enabling/disabling together
- Alias: Use the same chart multiple times with different names
Defining Dependencies
# Chart.yaml
apiVersion: v2
name: myapp
version: 1.0.0
dependencies:
- name: postgresql
version: "12.1.9"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
tags:
- database
- name: redis
version: "17.3.14"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
tags:
- cache
- name: common
version: "2.2.3"
repository: "https://charts.bitnami.com/bitnami"
# Use same chart with different configs
- name: postgresql
version: "12.1.9"
repository: "https://charts.bitnami.com/bitnami"
alias: postgresql-readonly
condition: postgresql-readonly.enabled
Common Commands/Patterns
# Download dependencies
helm dependency update ./mychart
# List dependencies
helm dependency list ./mychart
# Build dependencies (update lock file)
helm dependency build ./mychart
# Verify dependencies
helm lint ./mychart
Configuring Dependencies
# values.yaml
# Enable/disable dependencies
postgresql:
enabled: true
auth:
username: myuser
password: mypassword
database: mydb
primary:
persistence:
size: 10Gi
redis:
enabled: true
architecture: standalone
auth:
enabled: false
# Disable by tag
tags:
database: true
cache: false
Examples
Working with dependencies:
# Create chart with dependencies
helm create myapp
cd myapp
# Add dependencies to Chart.yaml
cat <<EOF >> Chart.yaml
dependencies:
- name: postgresql
version: "12.1.9"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
EOF
# Download dependencies
helm dependency update
# View downloaded dependencies
ls charts/
# Install with dependency values
helm install myapp . --set postgresql.enabled=true \
--set postgresql.auth.password=secretpass
Accessing dependency values in templates:
# Access subchart values
{{ .Values.postgresql.auth.database }}
# Import values from subchart
# Chart.yaml
dependencies:
- name: subchart
import-values:
- child: exports.data
parent: myimports
Repository Management
Helm repositories are servers that host chart packages for distribution and sharing.
Key Concepts
- Chart Repository: HTTP server hosting an
index.yamlfile and packaged charts - OCI Registry: Container registries that can store Helm charts
- Index File: YAML file containing metadata about available charts
Common Commands/Patterns
# Add a repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo add prometheus https://prometheus-community.github.io/helm-charts
# Add with authentication
helm repo add myrepo https://charts.example.com \
--username admin --password secret
# List repositories
helm repo list
# Update repository cache
helm repo update
# Remove a repository
helm repo remove stable
# Search for charts
helm search repo nginx
helm search repo nginx --versions
helm search repo nginx --version "^1.0.0"
# Search Artifact Hub
helm search hub wordpress
OCI Registry Support
# OCI support is enabled by default since Helm 3.8
# (only versions prior to 3.8 need: export HELM_EXPERIMENTAL_OCI=1)
# Login to registry
helm registry login registry.example.com
# Push chart to OCI registry
helm push mychart-1.0.0.tgz oci://registry.example.com/charts
# Pull chart from OCI registry
helm pull oci://registry.example.com/charts/mychart --version 1.0.0
# Install from OCI registry
helm install myrelease oci://registry.example.com/charts/mychart
Creating a Repository
# Package charts
helm package ./mychart
helm package ./anotherchart
# Generate index file
helm repo index . --url https://charts.example.com
# Update existing index
helm repo index . --url https://charts.example.com --merge index.yaml
Examples
Setting up GitHub Pages as chart repository:
# Create gh-pages branch
git checkout --orphan gh-pages
# Package and index charts
helm package ../charts/myapp
helm repo index . --url https://username.github.io/charts
# Commit and push
git add .
git commit -m "Initial chart repository"
git push origin gh-pages
# Add repository
helm repo add myrepo https://username.github.io/charts
Helm Hooks
Hooks allow you to intervene at certain points in a release's lifecycle to perform operations like running jobs before or after installation.
Key Concepts
flowchart TD
subgraph "Install Lifecycle"
A[helm install] --> B[pre-install hooks]
B --> C[Install resources]
C --> D[post-install hooks]
end
subgraph "Upgrade Lifecycle"
E[helm upgrade] --> F[pre-upgrade hooks]
F --> G[Upgrade resources]
G --> H[post-upgrade hooks]
end
subgraph "Delete Lifecycle"
I[helm uninstall] --> J[pre-delete hooks]
J --> K[Delete resources]
K --> L[post-delete hooks]
end
Available Hook Types:
pre-install: After templates are rendered, before any resources are createdpost-install: After all resources are loadedpre-delete: Before any resources are deletedpost-delete: After all resources are deletedpre-upgrade: After templates are rendered, before any resources are updatedpost-upgrade: After all resources are upgradedpre-rollback: After templates are rendered, before any resources are rolled backpost-rollback: After all resources are rolled backtest: Whenhelm testis invoked
Hook Annotations
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "myapp.fullname" . }}-db-migrate
annotations:
# Define hook type
"helm.sh/hook": pre-upgrade,pre-install
# Hook execution order (lower runs first)
"helm.sh/hook-weight": "-5"
# Hook deletion policy
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["./migrate.sh"]
Hook Delete Policies
before-hook-creation: Delete previous hook resource before new one is createdhook-succeeded: Delete after hook execution succeedshook-failed: Delete after hook execution fails
Examples
Database migration hook:
# templates/db-migrate-job.yaml
{{- if .Values.migrations.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "myapp.fullname" . }}-migrate
labels:
{{- include "myapp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
backoffLimit: 3
template:
metadata:
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command:
- /bin/sh
- -c
- |
echo "Running database migrations..."
python manage.py migrate --noinput
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "myapp.fullname" . }}-secret
key: database-url
{{- end }}
Post-install notification hook:
# templates/post-install-hook.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "myapp.fullname" . }}-notify
annotations:
"helm.sh/hook": post-install
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: notify
image: curlimages/curl:latest
command:
- curl
- -X
- POST
- -d
- '{"text":"{{ .Release.Name }} deployed successfully"}'
- "{{ .Values.slack.webhookUrl }}"
Test hook:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: {{ include "myapp.fullname" . }}-test
labels:
{{- include "myapp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
restartPolicy: Never
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "myapp.fullname" . }}:{{ .Values.service.port }}']
Best Practices for Chart Development
Guidelines for creating maintainable, secure, and reusable Helm charts.
Key Concepts
- Immutability: Charts should produce consistent results
- Configurability: Use values.yaml for all configurable options
- Documentation: Document all values and provide examples
- Security: Follow security best practices
Chart Structure Best Practices
# Use consistent naming with _helpers.tpl
{{- define "myapp.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 }}
# Always include standard labels
metadata:
labels:
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "myapp.chart" . }}
Values Best Practices
# values.yaml - Document all values
# -- Number of replicas for the deployment
replicaCount: 1
image:
# -- Container image repository
repository: nginx
# -- Image pull policy
pullPolicy: IfNotPresent
# -- Overrides the image tag (default is chart appVersion)
tag: ""
# -- Image pull secrets for private registries
imagePullSecrets: []
serviceAccount:
# -- Specifies whether a service account should be created
create: true
# -- Annotations to add to the service account
annotations: {}
# -- The name of the service account to use
name: ""
# -- Pod annotations
podAnnotations: {}
# -- Pod security context
podSecurityContext: {}
# fsGroup: 2000
# -- Container security context
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
# -- Resource requests and limits
resources: {}
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
Security Best Practices
# Set security contexts
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: {{ .Chart.Name }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Use resource limits
resources:
{{- toYaml .Values.resources | nindent 12 }}
# Use network policies
{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "myapp.fullname" . }}
spec:
podSelector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: {{ .Values.service.port }}
{{- end }}
Template Best Practices
# Use whitespace control
{{- if .Values.enabled }}
...
{{- end }}
# Use with for scoping
{{- with .Values.ingress }}
{{- if .enabled }}
...
{{- end }}
{{- end }}
# Provide sensible defaults
{{ .Values.name | default (include "myapp.fullname" .) }}
# Validate required values
{{ required "A valid .Values.database.host is required!" .Values.database.host }}
# Use comments for complex logic
{{/*
Calculate the number of replicas based on environment
*/}}
{{- $replicas := .Values.replicaCount -}}
{{- if eq .Values.environment "production" -}}
{{- $replicas = max 3 .Values.replicaCount -}}
{{- end -}}
# Use toYaml for complex structures
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- if .Values.extraEnv }}
{{- toYaml .Values.extraEnv | nindent 2 }}
{{- end }}
Examples
Well-structured NOTES.txt:
{{- $fullName := include "myapp.fullname" . -}}
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ (first $host.paths).path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ $fullName }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
Watch status with: kubectl get svc --namespace {{ .Release.Namespace }} -w {{ $fullName }}
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ $fullName }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ $fullName }} {{ .Values.service.port }}:{{ .Values.service.port }}
echo "Visit http://127.0.0.1:{{ .Values.service.port }}"
{{- end }}
Linting and testing:
# Lint chart
helm lint ./mychart
# Lint with strict mode
helm lint ./mychart --strict
# Template validation
helm template myrelease ./mychart | kubectl apply --dry-run=client -f -
# Run tests
helm test myrelease
# Use helm-unittest plugin
helm unittest ./mychart
Quick Reference
| Command | Description |
|---|---|
helm create <name> |
Create a new chart |
helm install <release> <chart> |
Install a chart |
helm upgrade <release> <chart> |
Upgrade a release |
helm upgrade --install <release> <chart> |
Install or upgrade |
helm rollback <release> [revision] |
Rollback to previous revision |
helm uninstall <release> |
Uninstall a release |
helm list |
List releases |
helm status <release> |
Show release status |
helm history <release> |
Show release history |
helm get values <release> |
Get release values |
helm get manifest <release> |
Get release manifests |
helm template <release> <chart> |
Render templates locally |
helm lint <chart> |
Lint a chart |
helm package <chart> |
Package a chart |
helm repo add <name> <url> |
Add a repository |
helm repo update |
Update repository cache |
helm search repo <keyword> |
Search repositories |
helm dependency update <chart> |
Update dependencies |
helm test <release> |
Run release tests |
helm show values <chart> |
Show chart's values |
Useful Flags
| Flag | Description |
|---|---|
-n, --namespace |
Kubernetes namespace |
-f, --values |
Specify values file |
--set |
Set values on command line |
--set-file |
Set values from file |
--dry-run |
Simulate an operation |
--debug |
Enable verbose output |
--wait |
Wait for resources to be ready |
--timeout |
Timeout for operations |
--atomic |
Rollback on failure |
--create-namespace |
Create namespace if not exists |
--reuse-values |
Reuse previous values |
--reset-values |
Reset to default values |
Common Issues and Solutions
| Issue | Cause | Solution |
|---|---|---|
Error: INSTALLATION FAILED: cannot re-use a name that is still in use |
Release name already exists | Use helm upgrade --install or uninstall existing release |
Error: UPGRADE FAILED: another operation is in progress |
Previous operation stuck | Run helm rollback or delete the release secret manually |
Error: rendered manifests contain a resource that already exists |
Resource created outside Helm | Adopt resource with meta.helm.sh/release-name annotations or manually delete it |
| Template produces invalid YAML | Incorrect indentation or syntax | Use helm template --debug to inspect output |
Error: chart requires kubeVersion: >=1.20.0 |
Kubernetes version too old | Upgrade cluster or modify Chart.yaml |
| Dependencies not downloading | Repository not added | Add repository with helm repo add |
| Values not being applied | Wrong value path or type | Check value structure with helm get values |
| Hook not executing | Wrong annotation or weight | Verify hook annotations and check helm history |
Error: INSTALLATION FAILED: timed out waiting |
Resources not becoming ready | Increase timeout or check pod logs |
| Secret data appearing in plain text | Missing base64 encoding | Use b64enc function in templates |
Debugging Tips
# View rendered templates
helm template myrelease ./mychart --debug
# Check release status
helm status myrelease
# View release manifest
helm get manifest myrelease
# View release values
helm get values myrelease
# View all release info
helm get all myrelease
# Check hooks
helm get hooks myrelease
# View release history
helm history myrelease
# Describe Kubernetes events
kubectl get events --sort-by='.lastTimestamp'
# Check pod logs
kubectl logs -l app.kubernetes.io/instance=myrelease
Common Template Errors
# Error: wrong indentation
# Bad
labels:
{{- include "myapp.labels" . }}
# Good
labels:
{{- include "myapp.labels" . | nindent 4 }}
# Error: nil pointer evaluating
# Bad
{{ .Values.config.key }}
# Good
{{ .Values.config.key | default "" }}
# Or use with
{{- with .Values.config }}
{{ .key }}
{{- end }}
# Error: wrong type for value
# Bad (when resources is a map)
resources: {{ .Values.resources }}
# Good
resources:
{{- toYaml .Values.resources | nindent 2 }}
Related Topics
The following topics complement Helm and are commonly used together in Kubernetes deployments:
- Kubernetes - Understanding Kubernetes resources is essential for effective Helm chart development
- Kustomize - Alternative approach to Kubernetes configuration management, often used alongside Helm
- ArgoCD - GitOps continuous delivery tool that deploys Helm charts from Git repositories
- Terraform - Infrastructure as code tool that can provision Kubernetes clusters and manage Helm releases
- Container Security - Best practices for securing container images and Kubernetes deployments used in charts
- Prometheus/Grafana - Monitoring stack commonly deployed via Helm charts with custom configurations