🚀 Introduction to Helm
What is Helm?
Helm is the package manager for Kubernetes, often called the "apt/yum/homebrew" for Kubernetes. It simplifies the deployment and management of applications on Kubernetes clusters.
Why Use Helm?
📦 Package Management
Bundle your K8s manifests into reusable packages called Charts
🔧 Configuration Management
Separate configuration from templates using values files
📋 Version Control
Track releases and easily rollback to previous versions
🔄 Reusability
Share charts publicly or within your organization
🏗️ Dependency Management
Manage complex applications with multiple dependencies
🎯 Templating
Powerful templating engine for dynamic configurations
Helm Architecture
Helm 3 Architecture (Current)
- Helm Client: Command-line tool that interacts with Kubernetes API
- No Tiller: Helm 3 removed Tiller (server component) for better security
- Release State: Stored as Kubernetes secrets in the namespace
- 3-way Strategic Merge: Better upgrade process comparing old manifest, new manifest, and live state
💻 Installation & Setup
Installing Helm
macOS
# Using Homebrew brew install helm # Using MacPorts sudo port install helm-3.0
Linux
# Using snap sudo snap install helm --classic # Using script curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # Using apt (Debian/Ubuntu) curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null sudo apt-get install apt-transport-https --yes echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list sudo apt-get update sudo apt-get install helm
Windows
# Using Chocolatey choco install kubernetes-helm # Using Scoop scoop install helm
Verify Installation
# Check Helm version helm version # Output example: version.BuildInfo{Version:"v3.13.0", GitCommit:"825e86f6", GitTreeState:"clean", GoVersion:"go1.20.8"}
Initial Setup
# Add the official Helm stable charts repo helm repo add stable https://charts.helm.sh/stable # Add Bitnami repo (popular charts) helm repo add bitnami https://charts.bitnami.com/bitnami # Update repo information helm repo update # List added repositories helm repo list
📚 Core Concepts
📊 Chart
A Helm package containing all resource definitions necessary to run an application, tool, or service inside Kubernetes
- Collection of YAML templates
- Default configuration values
- Dependencies on other charts
- Metadata about the package
🚀 Release
An instance of a chart running in a Kubernetes cluster
- Unique name within namespace
- Can have multiple releases of same chart
- Tracks deployment history
- Enables rollback capability
📁 Repository
Place where charts can be collected and shared
- HTTP server hosting index.yaml
- Can be public or private
- Similar to Docker Hub
- Supports authentication
⚙️ Values
Configuration data that can be passed to charts
- Override default configurations
- Environment-specific settings
- Can be provided via files or CLI
- Support complex data structures
📝 Templates
Go template files that generate Kubernetes manifests
- Dynamic resource generation
- Conditional logic support
- Loops and iterations
- Built-in and custom functions
🔌 Hooks
Allow chart developers to intervene at certain points in release lifecycle
- Pre/post install
- Pre/post upgrade
- Pre/post rollback
- Test hooks
Helm Workflow
📁 Chart Structure
Directory Structure
File Descriptions
Chart.yaml
apiVersion: v2 # Chart API version (required) name: mychart # Chart name (required) version: 0.1.0 # SemVer 2 version (required) # Optional fields kubeVersion: ">=1.19.0-0" # Kubernetes version constraint description: A Helm chart for my awesome application type: application # application or library keywords: - web - application home: https://example.com sources: - https://github.com/example/mychart dependencies: # Chart dependencies - name: nginx version: 1.2.3 repository: https://example.com/charts condition: nginx.enabled tags: - front-end import-values: - data maintainers: # Chart maintainers - name: John Doe email: john@example.com url: https://example.com icon: https://example.com/icon.png appVersion: "1.16.0" # Version of the app deprecated: false annotations: example: A chart annotation
values.yaml
# Default values for mychart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] nameOverride: "" fullnameOverride: "" 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: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 service: type: ClusterIP port: 80 ingress: enabled: false className: "" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific tls: [] resources: {} # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi autoscaling: enabled: false minReplicas: 1 maxReplicas: 100 targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 nodeSelector: {} tolerations: [] affinity: {}
values.schema.json
{
"$schema": "https://json-schema.org/draft-07/schema#",
"properties": {
"replicaCount": {
"description": "Number of replicas",
"type": "integer",
"minimum": 1
},
"image": {
"description": "Container Image",
"type": "object",
"properties": {
"repository": {
"type": "string",
"pattern": "^[a-z0-9-_/]+$"
},
"tag": {
"type": "string"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
}
}
}
}
.helmignore
# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store # Common VCS dirs .git/ .gitignore .bzr/ .bzrignore .hg/ .hgignore .svn/ # Common backup files *.swp *.bak *.tmp *.orig *~ # Various IDEs .project .idea/ *.tmproj .vscode/
📝 Working with Templates
Template Basics
Helm uses the Go template language with additional functions. Templates are stored in the templates/ directory.
Basic Template Example
apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "mychart.fullname" . }} labels: {{- include "mychart.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: {{- include "mychart.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} annotations: {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "mychart.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 80 protocol: TCP {{- if .Values.env }} env: {{- range $key, $value := .Values.env }} - name: {{ $key }} value: {{ $value | quote }} {{- end }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }}
Template Controls
Conditionals
# Simple if statement {{- if .Values.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress # ... ingress configuration ... {{- end }} # If-else statement replicas: {{ if .Values.autoscaling.enabled }}{{ .Values.autoscaling.minReplicas }}{{ else }}{{ .Values.replicaCount }}{{ end }} # Multiple conditions {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} apiVersion: v1 kind: PersistentVolumeClaim # ... PVC configuration ... {{- end }} # Checking for empty/nil values {{- if .Values.nodeSelector }} nodeSelector: {{- toYaml .Values.nodeSelector | nindent 2 }} {{- end }}
Loops
# Simple range loop env: {{- range $key, $value := .Values.env }} - name: {{ $key }} value: {{ $value | quote }} {{- end }} # Loop with index ports: {{- range $index, $port := .Values.service.ports }} - name: port-{{ $index }} port: {{ $port }} targetPort: {{ $port }} {{- end }} # Loop over list of objects {{- range .Values.ingress.hosts }} - host: {{ .host | quote }} http: paths: {{- range .paths }} - path: {{ .path }} pathType: {{ .pathType }} backend: service: name: {{ $.Release.Name }} port: number: {{ $.Values.service.port }} {{- end }} {{- end }}
Variables
# Define and use variables {{- $fullName := include "mychart.fullname" . -}} {{- $labels := include "mychart.labels" . -}} metadata: name: {{ $fullName }} labels: {{- $labels | nindent 4 }} # Variables in range loops {{- range $key, $value := .Values.secrets }} {{- $secretName := printf "%s-%s" $.Release.Name $key }} --- apiVersion: v1 kind: Secret metadata: name: {{ $secretName }} data: {{- range $k, $v := $value }} {{ $k }}: {{ $v | b64enc | quote }} {{- end }} {{- end }}
Template Functions (_helpers.tpl)
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "mychart.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 }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.chart" . }}
{{ include "mychart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
Interactive Template Demo
Try Template Rendering
🔧 Template Functions
Built-in Functions
String Functions
Text Manipulation
{{ .Values.name | upper }} # MYAPP
{{ .Values.name | lower }} # myapp
{{ .Values.name | title }} # Myapp
{{ .Values.name | trim }} # Remove whitespace
{{ .Values.name | trimPrefix "my" }}
{{ .Values.name | trimSuffix "app" }}
{{ .Values.name | replace "a" "b" }}
{{ .Values.name | nospace }} # Remove all spaces
String Operations
{{ .Values.name | quote }} # "myapp"
{{ .Values.name | squote }} # 'myapp'
{{ .Values.name | indent 4 }} # Indent 4 spaces
{{ .Values.name | nindent 4 }} # Newline + indent
{{ printf "%s-%s" .Release.Name .Chart.Name }}
{{ .Values.name | substr 0 5 }}
{{ .Values.name | trunc 63 }} # Truncate to 63 chars
Type Conversion Functions
{{ .Values.port | int }} # Convert to integer
{{ .Values.enabled | toString }} # Convert to string
{{ .Values.data | toJson }} # Convert to JSON
{{ .Values.data | toPrettyJson }} # Pretty JSON
{{ .Values.data | toYaml }} # Convert to YAML
{{ .Values.items | toStrings }} # Convert list items to strings
{{ .Values.number | float64 }} # Convert to float
{{ .Values.config | fromYaml }} # Parse YAML string
{{ .Values.config | fromJson }} # Parse JSON string
List Functions
{{ list "a" "b" "c" }} # Create a list
{{ .Values.items | first }} # Get first item
{{ .Values.items | last }} # Get last item
{{ .Values.items | rest }} # All except first
{{ .Values.items | initial }} # All except last
{{ .Values.items | append "d" }} # Add to end
{{ .Values.items | prepend "z" }} # Add to beginning
{{ .Values.items | reverse }} # Reverse order
{{ .Values.items | uniq }} # Remove duplicates
{{ .Values.items | sort }} # Sort list
{{ has "item" .Values.items }} # Check if contains
{{ .Values.items | join "," }} # Join with separator
Dictionary Functions
{{ dict "key1" "value1" "key2" "value2" }} # Create dict
{{ .Values.config | keys }} # Get all keys
{{ .Values.config | values }} # Get all values
{{ hasKey .Values.config "key" }} # Check if key exists
{{ get .Values.config "key" }} # Get value by key
{{ set .Values.config "key" "value" }} # Set key-value
{{ unset .Values.config "key" }} # Remove key
{{ merge .Values.config .Values.defaults }} # Merge dicts
{{ pick .Values.config "key1" "key2" }} # Pick keys
{{ omit .Values.config "key1" "key2" }} # Omit keys
Math Functions
{{ add 1 2 }} # 3
{{ sub 5 3 }} # 2
{{ mul 2 3 }} # 6
{{ div 10 2 }} # 5
{{ mod 10 3 }} # 1
{{ max 1 2 3 }} # 3
{{ min 1 2 3 }} # 1
{{ floor 3.14 }} # 3
{{ ceil 3.14 }} # 4
{{ round 3.14159 2 }} # 3.14
Encoding Functions
{{ .Values.secret | b64enc }} # Base64 encode
{{ .Values.encoded | b64dec }} # Base64 decode
{{ .Values.data | sha256sum }} # SHA256 hash
{{ .Values.data | sha1sum }} # SHA1 hash
{{ .Values.url | urlquery }} # URL encode
{{ .Values.url | urlunquery }} # URL decode
Flow Control Functions
# Default values {{ .Values.name | default "myapp" }} {{ default "default" .Values.undefined }} # Ternary operator {{ ternary "prod" "dev" .Values.production }} # Coalesce - first non-empty value {{ coalesce .Values.name .Values.nameOverride "default" }} # Empty check {{ if empty .Values.config }} # Config is empty or not set {{ end }} # Required - fail if not set {{ required "A valid .Values.database entry is required!" .Values.database }} # Fail with message {{ fail "This chart requires Kubernetes 1.20+" }}
Lookup Function
# Look up existing resources {{ $svc := lookup "v1" "Service" .Release.Namespace "myservice" }} {{ if $svc }} # Service exists, use its ClusterIP serviceIP: {{ $svc.spec.clusterIP }} {{ end }} # List all services in namespace {{ $services := lookup "v1" "Service" .Release.Namespace "" }} {{ range $services.items }} - {{ .metadata.name }} {{ end }} # Check if resource exists {{ if lookup "v1" "ConfigMap" .Release.Namespace "my-config" }} # ConfigMap exists {{ else }} # ConfigMap doesn't exist {{ end }}
🔗 Chart Dependencies
Defining Dependencies
apiVersion: v2 name: myapp version: 1.0.0 dependencies: - name: mysql version: 9.3.4 repository: https://charts.bitnami.com/bitnami condition: mysql.enabled tags: - database import-values: - child: auth.rootPassword parent: mysqlPassword - name: redis version: ~17.3.0 # Version range repository: https://charts.bitnami.com/bitnami condition: redis.enabled tags: - cache - name: common version: 1.x.x repository: file://../common # Local chart
Managing Dependencies
# Download dependencies to charts/ directory helm dependency update ./mychart # List dependencies helm dependency list ./mychart # Build dependencies from Chart.lock helm dependency build ./mychart # Update specific dependency helm dependency update ./mychart --skip-refresh
Configuring Dependencies
# Enable/disable dependencies mysql: enabled: true auth: rootPassword: "secretpassword" database: "myapp" primary: persistence: enabled: true size: 10Gi redis: enabled: false architecture: standalone auth: enabled: false # Global values accessible to all subcharts global: storageClass: "fast-ssd" postgresql: auth: postgresPassword: "globalpassword"
Conditional Dependencies
# In Chart.yaml dependencies: - name: mysql condition: mysql.enabled tags: - database - sql - name: postgresql condition: postgresql.enabled tags: - database - sql # In values.yaml tags: database: false # Disable all with 'database' tag mysql: enabled: true # Override tag setting postgresql: enabled: false
🪝 Helm Hooks
Hook Types
Pre-install
Executes after templates are rendered, but before any resources are created
Post-install
Executes after all resources are loaded into Kubernetes
Pre-delete
Executes on a deletion request before any resources are deleted
Post-delete
Executes on a deletion request after all resources have been deleted
Pre-upgrade
Executes on an upgrade request after templates are rendered
Post-upgrade
Executes on an upgrade after all resources have been upgraded
Pre-rollback
Executes on a rollback request after templates are rendered
Post-rollback
Executes on a rollback request after all resources have been modified
Test
Executes when the Helm test subcommand is invoked
Hook Examples
Database Migration Hook
apiVersion: batch/v1 kind: Job metadata: name: {{ include "myapp.fullname" . }}-db-migration annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "-5" "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: template: metadata: name: {{ include "myapp.fullname" . }}-db-migration spec: restartPolicy: Never containers: - name: db-migrate image: "{{ .Values.migration.image }}:{{ .Values.migration.tag }}" command: ["/bin/sh"] args: - "-c" - "python manage.py migrate --noinput" env: - name: DATABASE_URL value: {{ .Values.database.url | quote }}
Test Hook
apiVersion: v1 kind: Pod metadata: name: {{ include "myapp.fullname" . }}-test-connection annotations: "helm.sh/hook": test "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: containers: - name: test image: busybox command: ['wget'] args: ['{{ include "myapp.fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never
Hook Annotations
Hook Annotations Reference
helm.sh/hook- Specifies the hook type(s)helm.sh/hook-weight- Defines execution order (ascending)helm.sh/hook-delete-policy- When to delete hook resources:before-hook-creation- Delete before new hook is launchedhook-succeeded- Delete after hook succeedshook-failed- Delete if hook fails
📦 Chart Repositories
Working with Repositories
Repository Management
# 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 repository with authentication helm repo add private https://charts.example.com \ --username myuser \ --password mypass # List repositories helm repo list # Update repositories helm repo update # Remove a repository helm repo remove bitnami # Search charts in repositories helm search repo nginx helm search repo bitnami/mysql --versions
Creating Your Own Repository
Generate Repository Index
# Package your charts helm package ./mychart helm package ./anotherchart # Generate index.yaml helm repo index . --url https://charts.example.com # Update existing index helm repo index . --url https://charts.example.com --merge index.yaml
Repository Structure
apiVersion: v1 entries: mychart: - apiVersion: v2 appVersion: 1.16.0 created: "2023-10-01T10:00:00.000Z" description: A Helm chart for Kubernetes digest: sha256:1234567890abcdef name: mychart type: application urls: - https://charts.example.com/mychart-0.1.0.tgz version: 0.1.0 generated: "2023-10-01T10:00:00.000Z"
Hosting Options
GitHub Pages
Free hosting for public charts using GitHub Pages
# In your gh-pages branch
helm package ./mychart
helm repo index . --url https://username.github.io/charts
git add .
git commit -m "Update charts"
git push origin gh-pages
ChartMuseum
Open-source Helm Chart Repository server
# Install ChartMuseum
helm install chartmuseum stable/chartmuseum \
--set env.open.DISABLE_API=false \
--set persistence.enabled=true
Harbor
Enterprise-class registry for containers and Helm charts
Artifactory
Universal repository manager with Helm support
🚀 Advanced Topics
Library Charts
# Chart.yaml for library chart apiVersion: v2 name: common description: A Library Helm Chart for grouping common logic type: library # This makes it a library chart version: 1.0.0 # templates/_deployment.tpl {{- define "common.deployment" -}} apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "common.fullname" . }} labels: {{- include "common.labels" . | nindent 4 }} spec: # ... common deployment spec ... {{- end -}} # Using in another chart {{- include "common.deployment" . }}
Schema Validation
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["image", "service"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1,
"maximum": 10
},
"image": {
"type": "object",
"required": ["repository", "tag"],
"properties": {
"repository": {
"type": "string",
"pattern": "^[a-z0-9-_/]+$"
},
"tag": {
"type": "string",
"pattern": "^[a-z0-9.-]+$"
}
}
},
"service": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["ClusterIP", "NodePort", "LoadBalancer"]
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
}
}
}
}
}
Post Rendering
#!/bin/bash # kustomize-post-renderer.sh # Read manifests from stdin cat > base.yaml # Create kustomization.yaml cat > kustomization.yaml <# Apply kustomize and output result kustomize build . # Usage: # helm install myrelease ./mychart --post-renderer ./kustomize-post-renderer.sh
Helm Plugins
# Install helm-diff plugin helm plugin install https://github.com/databus23/helm-diff # Use helm diff helm diff upgrade myrelease ./mychart # Install helm-secrets plugin helm plugin install https://github.com/jkroepke/helm-secrets # Use helm secrets helm secrets enc secrets.yaml helm secrets install myrelease ./mychart -f secrets.yaml # List installed plugins helm plugin list # Update plugin helm plugin update diff # Remove plugin helm plugin uninstall diff
Advanced Templating Patterns
Named Templates with Parameters
{{- define "mychart.container" -}}
{{- $containerName := .containerName -}}
{{- $containerImage := .containerImage -}}
{{- $containerPort := .containerPort | default 8080 -}}
- name: {{ $containerName }}
image: {{ $containerImage }}
ports:
- containerPort: {{ $containerPort }}
{{- end -}}
# Usage:
containers:
{{- include "mychart.container" (dict "containerName" "app" "containerImage" .Values.image "containerPort" 3000) | nindent 2 }}
{{- include "mychart.container" (dict "containerName" "sidecar" "containerImage" .Values.sidecarImage) | nindent 2 }}
Complex Logic with Variables
{{- $replicas := .Values.replicaCount -}}
{{- if .Values.autoscaling.enabled -}}
{{- $replicas = .Values.autoscaling.minReplicas -}}
{{- end -}}
{{- $serviceName := printf "%s-svc" (include "mychart.fullname" .) -}}
{{- $servicePort := .Values.service.port | int -}}
{{- range $i := until (int $replicas) }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $serviceName }}-{{ $i }}
spec:
selector:
statefulset.kubernetes.io/pod-name: {{ include "mychart.fullname" $ }}-{{ $i }}
ports:
- port: {{ $servicePort }}
{{- end }}
⭐ Best Practices
📋 Chart Development Best Practices
- Use Semantic Versioning: Follow SemVer for chart versions (MAJOR.MINOR.PATCH)
- Document Everything: Include comprehensive README.md and NOTES.txt
- Default Values: Provide sensible defaults in values.yaml
- Schema Validation: Use values.schema.json for input validation
- Label Consistency: Use standard Kubernetes labels
- Resource Limits: Always set resource requests and limits
- Security Context: Run containers as non-root by default
🏗️ Template Best Practices
- Use _helpers.tpl: Define reusable template snippets
- Avoid Hardcoding: Make everything configurable via values
- Quote Strings: Always quote string values in templates
- Whitespace Control: Use {{- and -}} to control whitespace
- Fail Fast: Use 'required' function for mandatory values
- Comments: Document complex template logic
🔒 Security Best Practices
- Image Tags: Never use 'latest' tag in production
- Secrets Management: Don't store secrets in values.yaml
- RBAC: Include proper RBAC resources
- Network Policies: Include network policies when needed
- Pod Security: Set appropriate security contexts
- Image Pull Secrets: Support private registries
📦 Release Management
- Atomic Upgrades: Use --atomic flag for production upgrades
- Dry Run: Always test with --dry-run first
- Rollback Plan: Document rollback procedures
- Release Notes: Maintain detailed release notes
- Testing: Include helm test hooks
- Backup: Backup releases before major upgrades
Common Patterns
Multi-Environment Configuration
# values-dev.yaml environment: development replicaCount: 1 resources: limits: memory: 256Mi cpu: 100m # values-prod.yaml environment: production replicaCount: 3 resources: limits: memory: 1Gi cpu: 500m # Install with environment-specific values helm install myapp ./mychart -f values-prod.yaml
GitOps-Friendly Structure
deployments/
├── base/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
├── environments/
│ ├── dev/
│ │ ├── values.yaml
│ │ └── secrets.yaml (encrypted)
│ ├── staging/
│ │ ├── values.yaml
│ │ └── secrets.yaml (encrypted)
│ └── prod/
│ ├── values.yaml
│ └── secrets.yaml (encrypted)
└── scripts/
├── deploy.sh
└── rollback.sh
🏋️ Interactive Exercises
Exercise 1: Helm Concepts Quiz
Exercise 2: Template Functions
Exercise 3: Create Your First Chart
Exercise 4: Hook Ordering
Exercise 5: Debug a Chart
Fix the issues in this Chart.yaml:
📚 Additional Resources
Official Documentation
Chart Repositories
Tools & Utilities
🎉 Congratulations!
You've completed the comprehensive Helm Charts guide. You now have the knowledge to:
- Create and structure Helm charts
- Write complex templates with functions and logic
- Manage dependencies and hooks
- Set up and work with chart repositories
- Follow best practices for production deployments
Keep practicing and exploring advanced features to become a Helm expert!