This guide teaches you Helm — the package manager for Kubernetes. Helm simplifies deploying complex applications by templating Kubernetes manifests and providing a clean package format. This is vendor-neutral education; Helm works with any Kubernetes cluster.
graph LR Chart["Helm Chart<br/>Templates<br/>Values"] --> Template["Template<br/>Engine"] Template --> K8s["Kubernetes<br/>Manifests<br/>YAML"] K8s --> Deploy["kubectl apply"] Deploy --> Pod["Running Pod<br/>in K8s"] Config["values-dev.yaml<br/>values-prod.yaml"] Config --> Template style Chart fill:#e3f2fd style Pod fill:#c8e6c9Table of Contents
- Why Helm Exists
- What is Helm
- Core Concepts
- Chart Structure
- Installing Charts
- Values and Overrides
- Template Syntax
- Repositories
- Common Commands
- When to Use What
- Troubleshooting
- Next Steps
Why Helm Exists
Managing Kubernetes manifests manually creates several problems that grow exponentially as an organization scales. Configuration explosion is the first major problem. Organizations deploying a single application across multiple environments face immediate configuration challenges because different environments require different resource limits, different replica counts that reflect traffic patterns, different database URLs for environment-specific databases, different image versions as versions roll out gradually, and different namespace names for environment separation. Without templating, organizations create three separate YAML files (deployment-dev.yaml, deployment-staging.yaml, deployment-prod.yaml) with slightly different configuration. When application code changes, the update must be replicated across all three files, and environments easily diverge as some files are updated while others are forgotten.
Template duplication is the second major problem. A Kubernetes manifest for a single application is rarely a single file. A typical application manifest includes a Deployment, Service, ConfigMap, Secret, Ingress, NetworkPolicy, and PodDisruptionBudget—seven or more related files that must be maintained together and kept in sync. When an organization deploys 20 microservices, this creates 140 or more files that must be managed while maintaining relationships between them. Helm solves this problem by using a single chart that generates all required manifest files from unified templates and configuration values.
Dependency management is the third major problem. Real applications have dependencies beyond application code. An application typically needs a database such as PostgreSQL and a message queue such as RabbitMQ to function. Deploying these dependencies requires creating separate manifests for the PostgreSQL Deployment, Service, and Secrets, along with the RabbitMQ Deployment, Service, and ConfigMap, then carefully configuring the application to connect correctly. Helm solves this by allowing charts to declare dependencies on other charts, which automatically installs and configures PostgreSQL and RabbitMQ as part of the application deployment.
Sharing and reuse represent the fourth major problem. Teams often discover that colleagues have already solved particular deployment challenges. If someone has built a PostgreSQL deployment they're happy with, the manual approach requires copying manifests into your project and hoping they maintain it—which rarely happens as projects diverge. Helm provides a better approach: teams add a chart repository, install charts from it, and reuse well-maintained charts across all projects while automatically receiving updates and fixes.
Helm is a package manager and templating system for Kubernetes that brings application package management concepts to container orchestration. Helm packages are called charts, which are analogous to npm packages for Node.js or Homebrew formulas for Mac software, providing a standardized way to distribute Kubernetes applications. Charts contain templates with placeholders for configuration values, allowing a single chart to be customized for different environments without duplicating template code. Helm maintains version information for charts, enabling organizations to install specific versions and pin versions for reproducibility. Charts can declare dependencies on other charts, enabling complex applications composed of multiple sub-components to be deployed as a single coordinated unit. Helm tracks which charts have been installed as releases in your cluster and manages upgrades and rollbacks, allowing safe version transitions with easy rollback if needed.
Key Terminology
A chart is a Helm package containing templates, configuration defaults, metadata about the chart, and documentation. A release is a running instance of a chart in your cluster with specific configuration values applied. For example, a single chart called "nginx" (the generic template) can have multiple releases running simultaneously: Release 1 called "my-web-server" (nginx configured for one application) and Release 2 called "your-web-server" (nginx configured for another application).
Values are configuration data stored in a YAML file that get merged with chart defaults to customize behavior for a specific environment or use case. Templates are Kubernetes manifests containing placeholders for values—these placeholders get filled in when the chart is deployed. A repository is a hosted location where charts are stored and distributed, analogous to Docker Hub for container images.
Core Concepts
1. Charts
A chart is a Helm package with a standard directory structure containing several key files and directories. Chart.yaml provides metadata about the chart itself, such as name, version, and description. values.yaml provides default configuration values used when the chart is deployed. The templates directory contains Kubernetes manifest templates including deployment.yaml for the application deployment, service.yaml for network exposure, configmap.yaml for configuration, _helpers.tpl for reusable template helper functions, and a tests subdirectory for deployment verification. The charts directory contains dependency charts such as postgresql that this chart depends on.
Chart.yaml: Metadata about the chart
apiVersion: v2name: my-appdescription: My application charttype: applicationversion: 1.2.0 # Chart versionappVersion: 1.0 # Application versionhome: https://github.com/me/my-appmaintainers: - name: Your Name email: you@example.comvalues.yaml: Default configuration
replicaCount: 3 image: repository: my-app tag: "1.0" service: type: ClusterIP port: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi database: host: localhost port: 54322. Templates
Kubernetes manifests with Helm template syntax (placeholders).
templates/deployment.yaml:
apiVersion: apps/v1kind: Deploymentmetadata: name: {{ include "my-app.fullname" . }}spec: replicas: {{ .Values.replicaCount }} template: spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" ports: - containerPort: {{ .Values.service.port }} resources: requests: cpu: {{ .Values.resources.requests.cpu }} memory: {{ .Values.resources.requests.memory }} limits: cpu: {{ .Values.resources.limits.cpu }} memory: {{ .Values.resources.limits.memory }}templates/service.yaml:
apiVersion: v1kind: Servicemetadata: name: {{ include "my-app.fullname" . }}spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} targetPort: {{ .Values.service.port }} selector: app: {{ include "my-app.name" . }}3. Releases
A release is a deployed instance of a chart with specific values applied. When you install a chart with the helm install command, you create a release. For example, installing the my-app chart creates a release called my-server and generates Kubernetes resources from the templates filled in with the default values. When you update the chart and upgrade the release with the helm upgrade command, all Kubernetes resources are updated to match the new chart template.
Chart Structure
A minimal chart requires a directory named my-app/ containing three key files: Chart.yaml for metadata, values.yaml for default configuration, and a templates/ directory containing at least one Kubernetes manifest template like deployment.yaml.
A production-quality Helm chart contains additional components for completeness. Chart.yaml is a required file containing metadata about the chart including name, version, and description. values.yaml is a required file containing default configuration values. Optional values-dev.yaml and values-prod.yaml files provide environment-specific overrides for development and production environments. README.md provides documentation explaining what the chart does and how to use it. The charts/ directory contains dependency charts such as a PostgreSQL database chart. The templates/ directory contains Kubernetes manifest templates that will be rendered with values, including NOTES.txt which displays helpful post-installation notes, _helpers.tpl which defines reusable template helper functions, and individual templates for Kubernetes resources such as deployment.yaml, service.yaml, configmap.yaml, secret.yaml, and ingress.yaml. Additional templates may include hpa.yaml for Horizontal Pod Autoscalers and pdb.yaml for Pod Disruption Budgets, as well as a tests/ subdirectory containing test templates. Finally, .helmignore operates similarly to .gitignore by excluding certain files from the packaged chart.
Chart.yaml Fields
apiVersion: v2 # Helm API version (v2 recommended)name: my-appversion: 1.2.0 # Chart version (semantic versioning)appVersion: 1.0.5 # Application version being packageddescription: My applicationtype: application # or "library" for sub-charts home: https://github.com/me/my-appsources: - https://github.com/me/my-appmaintainers: - name: Your Name email: you@example.com - name: Co-maintainer email: other@example.com keywords: - web - application dependencies: # Charts this chart depends on - name: postgresql version: 12.0.0 repository: https://charts.bitnami.com/bitnamivalues.yaml Philosophy
Default values should be production-safe with secure and reasonable default settings, documented with comments explaining each value's purpose and impact, and structured with values grouped logically by function or component.
# Number of replicas (adjust based on traffic)replicaCount: 3 image: # Container image repository repository: my-app # Image tag (overrides default) tag: "1.0" # Pull policy: Always, IfNotPresent, Never pullPolicy: IfNotPresent # Service configurationservice: # Service type: ClusterIP, NodePort, LoadBalancer type: ClusterIP # Service port port: 80 # Resource requests and limitsresources: limits: cpu: 500m memory: 512Mi requests: cpu: 100m memory: 128Mi # Enable autoscalingautoscaling: enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 80 # Database configuration (external or via dependency)database: enabled: true host: postgresql port: 5432Installing Charts
Installing from Local Directory
# Install from a chart directoryhelm install my-release ./my-app # With custom valueshelm install my-release ./my-app \ --values values-prod.yaml \ --set replicaCount=5 # To a specific namespacehelm install my-release ./my-app \ --namespace production \ --create-namespaceInstalling from a Repository
# Add a chart repositoryhelm repo add bitnami https://charts.bitnami.com/bitnamihelm repo update # Search for chartshelm search repo postgresql # Install from repositoryhelm install my-db bitnami/postgresqlRelease States
After installation, you can list releases with helm list, get the release status with helm status my-release, retrieve the release values with helm get values my-release, see the generated manifests that Helm created with helm get manifest my-release, and get the chart's default values with helm show values bitnami/postgresql.
Upgrading Releases
You can upgrade releases with a new values file using helm upgrade my-release ./my-app -f values-prod.yaml, or upgrade with value overrides using helm upgrade my-release ./my-app --set replicaCount=10 --set image.tag=2.0. To check what changes will occur before upgrading, use helm upgrade my-release ./my-app --dry-run --debug, or perform an atomic upgrade that rolls back if it fails with helm upgrade my-release ./my-app --atomic.
Rollback Releases
You can see release history with helm history my-release, which shows all previous releases with revision numbers. To roll back to the previous release, use helm rollback my-release, or to roll back to a specific revision, use helm rollback my-release 2.
Uninstalling Releases
You can delete a release with helm uninstall my-release, or delete while preserving release history for auditing purposes with helm uninstall my-release --keep-history. Note that you cannot rollback a deleted release; use helm install to reinstall if needed.
Values and Overrides
Values are merged in a specific order where later values override earlier ones: first come chart defaults from values.yaml, then parent chart values if a dependency is involved, then file overrides from -f values.yaml, and finally command-line overrides from --set key=value.
File-Based Overrides
Create separate value files for environments:
values.yaml (production defaults)
replicaCount: 3image: tag: "latest"resources: requests: cpu: 100m memory: 128Mivalues-dev.yaml (development overrides)
replicaCount: 1image: tag: "dev-latest"resources: requests: cpu: 50m memory: 64Mivalues-prod.yaml (production overrides)
replicaCount: 5image: tag: "v1.2.0" # Pinned version for prodresources: requests: cpu: 500m memory: 512MiInstall with overrides:
# Developmenthelm install app ./my-app -f values-dev.yaml # Productionhelm install app ./my-app -f values-prod.yamlCommand-Line Overrides
# Set a single valuehelm install my-release ./my-app --set replicaCount=5 # Set nested values (use dot notation)helm install my-release ./my-app \ --set image.repository=my-app \ --set image.tag=2.0 \ --set resources.requests.memory=256Mi # Set array valueshelm install my-release ./my-app \ --set-list 'nodeSelector[node_role]=worker,nodeSelector[environment]=prod' # Combine file and command-line overrideshelm install my-release ./my-app \ -f values-prod.yaml \ --set replicaCount=10Template Syntax
Helm templates use Go templating syntax. Common patterns:
Variable Substitution
Template:
replicaCount: {{ .Values.replicaCount }}With values replicaCount: 3:
replicaCount: 3Accessing Values
# Simple valuename: {{ .Values.name }} # Nested value (dot notation)image: {{ .Values.image.repository }}:{{ .Values.image.tag }} # Chart metadatachartName: {{ .Chart.Name }}chartVersion: {{ .Chart.Version }}appVersion: {{ .Chart.AppVersion }}releaseName: {{ .Release.Name }}releaseNamespace: {{ .Release.Namespace }}Conditionals
Template:
{{ if .Values.autoscaling.enabled }}apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: {{ include "my-app.fullname" . }}spec: minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }}{{ end }}With autoscaling.enabled: false, this entire section is removed.
Loops
Template:
env:{{ range $key, $value := .Values.environment }} - name: {{ $key }} value: {{ $value }}{{ end }}With values:
environment: LOG_LEVEL: "debug" APP_MODE: "development"Output:
env: - name: LOG_LEVEL value: "debug" - name: APP_MODE value: "development"Template Functions
Helm provides built-in functions:
# String functions{{ .Values.name | upper }} # UPPERCASE{{ .Values.description | lower }} # lowercase{{ .Values.message | quote }} # "message"{{ .Values.list | join "," }} # item1,item2,item3 # Conditionals{{ if .Values.enabled }}...{{ end }}{{ if and .Values.a .Values.b }}...{{ end }}{{ if or .Values.a .Values.b }}...{{ end }}{{ if not .Values.disabled }}...{{ end }} # Math{{ add 1 2 }} # 3{{ mulf .Values.a 2 | int }} # a * 2 # Defaults{{ .Values.optional | default "fallback" }} # Templates (sub-templates){{ include "my-app.labels" . }}Helpers
Define reusable template snippets in _helpers.tpl:
{{/*Expand the name of the chart.*/}}{{- define "my-app.name" -}}{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}{{- end }} {{/*Create a fully qualified app name.*/}}{{- define "my-app.fullname" -}}{{- if .Values.fullnameOverride }}{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}{{- else }}{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}{{- end }}{{- end }} {{/*Common labels*/}}{{- define "my-app.labels" -}}helm.sh/chart: {{ include "my-app.chart" . }}{{ include "my-app.selectorLabels" . }}app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}app.kubernetes.io/managed-by: {{ .Release.Service }}{{- end }} {{/*Selector labels*/}}{{- define "my-app.selectorLabels" -}}app.kubernetes.io/name: {{ include "my-app.name" . }}app.kubernetes.io/instance: {{ .Release.Name }}{{- end }}Usage in templates:
apiVersion: apps/v1kind: Deploymentmetadata: name: {{ include "my-app.fullname" . }} labels: {{- include "my-app.labels" . | nindent 4 }}Repositories
Charts are distributed through repositories (like Docker Hub for images).
Adding Repositories
You can add a repository with commands like helm repo add bitnami https://charts.bitnami.com/bitnami and helm repo add stable https://charts.helm.sh/stable. List all configured repositories with helm repo list, and update the repository cache with helm repo update.
Searching Repositories
You can search for a chart with helm search repo postgresql, search across all added repositories with helm search repo nginx, and show detailed chart information with commands like helm show chart bitnami/postgresql, helm show values bitnami/postgresql, and helm show readme bitnami/postgresql.
Common Public Repositories
Repository | URL | Use Cases |
|---|---|---|
Bitnami | Databases, message queues, infrastructure | |
Stable (deprecated) | Legacy charts | |
ChartMuseum | Self-hosted chart repository | |
Cloud providers | AWS, GCP, Azure | Provider-specific charts |
Private Repositories
You can add a public repository with helm repo add myrepo https://charts.mycompany.com, and for private repositories, use OCI registry authentication with helm registry login myregistry.example.com --username myuser --password mypassword.
Common Commands
Chart Management
You can create a new chart with helm create my-app, validate a chart with helm lint my-app, and check what will be deployed using a dry-run with helm install my-release ./my-app --dry-run --debug. To get the calculated values, use helm template my-release ./my-app -f values-prod.yaml, and to package a chart, use helm package my-app, which creates a file like my-app-1.0.0.tgz.
Release Management
Command | What It Does |
|---|---|
| Create a new release |
| Update existing release |
| Create or update (idempotent) |
| Revert to previous version |
| Delete a release |
| Show all releases |
| Show release history |
| Current release status |
Inspection Commands
You can get generated manifests with helm get manifest my-release, get release values with helm get values my-release, retrieve all release data with helm get all my-release, and get release notes with helm get notes my-release.
Testing
You can run chart tests (if defined in the chart) with helm test my-release, lint the chart for errors with helm lint my-app, and perform a dry-run with debug output using helm install my-release ./my-app --dry-run --debug.
When to Use What
Helm vs Raw Manifests
Use Helm when deploying complex applications with multiple resources, needing environment-specific configuration for dev/staging/prod environments, installing third-party applications, wanting version control and rollback capabilities, or needing to share charts across teams for consistency. Use raw manifests for very simple applications with a single deployment, one-off non-production deployments, when learning Kubernetes basics, or when templating is not required.
Helm vs Kustomize
Both Helm and Kustomize solve similar problems but take different approaches. Helm offers a full templating language that is more powerful but has a larger learning curve, includes package management with repositories and versioning, and works better for complex multi-app deployments. Kustomize uses an overlay-based approach that is simpler and more composable, is built into kubectl without requiring a separate tool, has a smaller learning curve, and works better for single applications with variants.
Helm-like alternatives include ArgoCD, which provides GitOps-focused deployment orchestration and uses Helm internally; Flux, which offers GitOps continuous delivery and also uses Helm internally; and Kustomize, which provides Kubernetes-native configuration management in a complementary way.
Recommendation
Start with raw manifests to learn Kubernetes basics. Graduate to Helm for managing multiple applications. Consider Kustomize for simple overlays. Adopt ArgoCD or Flux if doing GitOps at scale.
Troubleshooting
Issue: "Release not found"
If you encounter this error, you may be using the wrong release name. Use helm list to find the correct name, or if the release is in a different namespace, use helm list --all-namespaces.
Issue: "Template rendering failed"
Check template syntax with helm lint my-app. To debug rendering, use helm template my-release ./my-app --debug, or validate values with helm show values bitnami/postgresql | helm template my-release ./my-app -f -.
Issue: "Deployment doesn't use updated values"
Verify the current values with helm get values my-release. Check what changed with helm upgrade my-release ./my-app --dry-run, then upgrade with new values using helm upgrade my-release ./my-app -f values-prod.yaml.
Issue: "Chart has broken dependencies"
Update chart dependencies with helm dependency update my-app. Check dependencies with helm dependency list my-app, or lint to see issues with helm lint my-app.
Issue: "Rollback failed"
See what revisions exist with helm history my-release. Try rolling back to a specific revision with helm rollback my-release 2. If an atomic upgrade fails, check pod status with kubectl describe pods -l app=my-app and view logs with kubectl logs <pod-name>.
Quick Reference
Installation Flow
Follow this flow to add a repository, check what will install, do a dry-run to verify, install for real, and verify the results. First, add the repository and update it with helm repo add myrepo https://... and helm repo update. Next, check what will install by running helm search repo myapp and helm show values myrepo/myapp > my-values.yaml, then edit my-values.yaml as needed. Perform a dry-run to verify with helm install myapp myrepo/myapp -f my-values.yaml --dry-run --debug. When ready, install for real with helm install myapp myrepo/myapp -f my-values.yaml. Finally, verify with helm status myapp and kubectl get pods.
Upgrade Flow
To upgrade a release, first check what changed with helm repo update and helm upgrade myapp myrepo/myapp --dry-run. When ready to proceed, upgrade with helm upgrade myapp myrepo/myapp. If something breaks, quickly roll back with helm rollback myapp.
Creating a Chart
To create a new chart, generate the scaffold with helm create my-app. Edit the Chart.yaml, values.yaml, and template files in templates/. Validate the chart with helm lint my-app and helm template my-app --debug. Install locally for testing with helm install my-release ./my-app. When ready to distribute, package the chart with helm package my-app.
Next Steps: Deploy Applications with Helm
For practical deployment, use Helm in your actual deployments by following the end-to-end secure deployment guide to deploy a real app with Helm, exploring language-specific examples for Python, Node.js, Go, and Java, and diving deeper into Helm operations.
To secure your Helm deployments, add security to your charts by learning about security contexts in Helm values, controlling traffic with network policies, handling secrets securely in Helm, and implementing immutable filesystems in charts.
For advanced Helm topics, expand your knowledge by exploring advanced Helm patterns, keeping Helm-deployed applications patched with an upgrade and patching playbook, and managing images across multiple cloud environments.
Resources
Refer to the Helm official documentation at https://helm.sh/docs/, search for charts at Artifact Hub (https://artifacthub.io/), and follow Helm best practices at https://helm.sh/docs/chart_best_practices/. The Helm template guide (https://helm.sh/docs/chart_template_guide/) provides detailed information on templating, and community charts are available at https://github.com/helm/charts.
You now have the foundation to package, distribute, and deploy Kubernetes applications with Helm. Practice by installing some public charts from Artifact Hub, then graduate to writing your own custom charts for your organization's applications.
