Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

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.

Helm ArchitectureHelm CLIChart RepositoryKubernetes APIChart PackageTemplatesValuesChart.yamlRendered ManifestsReleaseHelm ArchitectureHelm CLIChart RepositoryKubernetes APIChart PackageTemplatesValuesChart.yamlRendered ManifestsRelease

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
Chart Directory Structuremychart/Chart.yamlvalues.yamlcharts/templates/.helmignoredeployment.yamlservice.yaml_helpers.tplNOTES.txttests/Chart Directory Structuremychart/Chart.yamlvalues.yamlcharts/templates/.helmignoredeployment.yamlservice.yaml_helpers.tplNOTES.txttests/

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

helm installSuccessErrorhelm upgradehelm uninstallNew revisionhelm upgrade --installPendingDeployedFailedSupersededUninstalledhelm installSuccessErrorhelm upgradehelm uninstallNew revisionhelm upgrade --installPendingDeployedFailedSupersededUninstalled
  • 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

values.yamlTemplate EngineBuilt-in ObjectsSprig FunctionsTemplate FilesRendered YAMLvalues.yamlTemplate EngineBuilt-in ObjectsSprig FunctionsTemplate FilesRendered 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.yaml file 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

Delete Lifecyclehelm uninstallpre-delete hooksDelete resourcespost-delete hooksUpgrade Lifecyclehelm upgradepre-upgrade hooksUpgrade resourcespost-upgrade hooksInstall Lifecyclehelm installpre-install hooksInstall resourcespost-install hooksDelete Lifecyclehelm uninstallpre-delete hooksDelete resourcespost-delete hooksUpgrade Lifecyclehelm upgradepre-upgrade hooksUpgrade resourcespost-upgrade hooksInstall Lifecyclehelm installpre-install hooksInstall resourcespost-install hooks

Available Hook Types:

  • pre-install: After templates are rendered, before any resources are created
  • post-install: After all resources are loaded
  • pre-delete: Before any resources are deleted
  • post-delete: After all resources are deleted
  • pre-upgrade: After templates are rendered, before any resources are updated
  • post-upgrade: After all resources are upgraded
  • pre-rollback: After templates are rendered, before any resources are rolled back
  • post-rollback: After all resources are rolled back
  • test: When helm test is 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 created
  • hook-succeeded: Delete after hook execution succeeds
  • hook-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:

  1. Kubernetes - Understanding Kubernetes resources is essential for effective Helm chart development
  2. Kustomize - Alternative approach to Kubernetes configuration management, often used alongside Helm
  3. ArgoCD - GitOps continuous delivery tool that deploys Helm charts from Git repositories
  4. Terraform - Infrastructure as code tool that can provision Kubernetes clusters and manage Helm releases
  5. Container Security - Best practices for securing container images and Kubernetes deployments used in charts
  6. Prometheus/Grafana - Monitoring stack commonly deployed via Helm charts with custom configurations