AB
A guide to understanding Helm, a package manager for Kubernetes, and how it simplifies the deployment and management of applications in a Kubernetes cluster.
Helm is a package manager for Kubernetes that simplifies the deployment and management of applications in a Kubernetes cluster. Just like apt
or yum
helps manage software packages in Linux, or npm
helps manage dependencies in JavaScript projects, Helm manages Kubernetes applications as packages called charts.
Deploying applications in Kubernetes involves managing many YAML files. This can be:
Helm solves these challenges by:
Deploying a simple NGINX application requires at least two YAML files:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
Commands to apply these files:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Outcome:
Using Helm, you can package the above YAML files into a chart and deploy it in a single command.
Command to Install NGINX Using Helm:
helm install my-nginx stable/nginx
my-nginx
: The release name.stable/nginx
: The Helm chart name (from a public Helm chart repository).Outcome:
Why Is This Better?
replicas
or image
based on the environment.Helm is called a package manager because it bundles all the files (YAML) needed to deploy an application into a single package called a chart. This chart can be reused, shared, and customized, just like software packages on your operating system.
A Helm chart has a specific structure. Here’s an example:
mychart/
Chart.yaml # Metadata about the chart
values.yaml # Default configuration values
templates/ # Kubernetes YAML files with templating
deployment.yaml # Example deployment template
service.yaml # Example service template
Each file plays a role:
replicas: 2
.Deployment template in templates/deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: { { .Release.Name } }
spec:
replicas: { { .Values.replicas } }
template:
spec:
containers:
- name: nginx
image: { { .Values.image } }
Corresponding values.yaml
:
replicas: 2
image: nginx:1.21
Command to deploy:
helm install my-nginx ./mychart
Outcome:
{{ .Values.replicas }}
with values from values.yaml
.nginx:1.21
.To effectively use Helm, you need to meet certain prerequisites. This section ensures you’re prepared to get started, even if you’re new to Kubernetes.
Before diving into Helm, you should have a foundational understanding of Kubernetes. Specifically:
Analogy: Think of a Kubernetes cluster as a factory:
If you want to deploy a web application, Kubernetes will:
To use Helm, you need to install it on your local machine. Here’s how to do it for different operating systems:
C:\Program Files\Helm
).helm version
brew install helm
helm version
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
If the helm
command doesn’t work after installation, you might need to add the directory containing the Helm binary to your system’s PATH. For example, on Linux:
export PATH=$PATH:/path/to/helm/directory
Helm requires access to a Kubernetes cluster to deploy applications. Here are two options:
You can use tools like Minikube or Kind to set up a local cluster.
Minikube:
minikube start
kubectl cluster-info
Kind:
kind create cluster
If you prefer a managed solution, you can use cloud providers like AWS (EKS), Azure (AKS), or Google Cloud (GKE).
gcloud container clusters create my-cluster --num-nodes=3
gcloud container clusters get-credentials my-cluster
This section introduces key Helm concepts to build a strong foundation. We’ll break down terms like Charts, Releases, Repositories, and Values with examples and easy-to-understand analogies.
A Helm chart is like a blueprint or package for deploying an application in Kubernetes. It contains all the necessary YAML files (manifests) and configuration details required to set up and manage the application.
Analogy: Think of a Helm chart as a recipe for cooking a dish. The recipe includes:
A Helm chart has the following structure:
my-chart/
Chart.yaml # Metadata about the chart (name, version, etc.)
values.yaml # Default configuration values
templates/ # Kubernetes YAML templates
charts/ # Dependency charts
Let’s say you’re deploying a web application using a Helm chart:
apiVersion: v2
name: my-web-app
version: 0.1.0
description: A simple web app
replicas: 2
image:
repository: nginx
tag: latest
apiVersion: apps/v1
kind: Deployment
metadata:
name: { { .Chart.Name } }
spec:
replicas: { { .Values.replicas } }
template:
spec:
containers:
- name: nginx
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
Command:
helm install my-app ./my-chart
Outcome: Deploys an application named my-app
to your Kubernetes cluster with 2 replicas of the nginx
container.
A release is an instance of a Helm chart deployed to a Kubernetes cluster. When you install a chart, it becomes a release with a unique name.
Analogy: A chart is like a software package, and a release is the installed version running on your system.
helm install frontend ./my-chart
helm install backend ./my-chart --set image.repository=my-backend
frontend
: A release for the frontend with default configurations.backend
: A release for the backend using a different image.A Helm repository is like a library where Helm charts are stored. Helm fetches charts from these repositories for installation.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm search repo bitnami
Outcome: Lists all available charts in the Bitnami repository.
You can host your own Helm repository:
helm package ./my-chart
.tgz
package for your chart.Values in Helm are configuration parameters that allow you to customize a chart during deployment. The default values are defined in the values.yaml
file, but you can override them.
helm install my-app ./my-chart --set replicas=3,image.tag=1.21
1.21
.You can create a custom values file for complex configurations:
replicas: 5
image:
repository: my-custom-image
tag: 1.0.0
helm install my-app ./my-chart -f custom-values.yaml
custom-values.yaml
.By understanding these concepts, you’re now equipped to start deploying and managing applications in Kubernetes using Helm!
Now that we understand what Helm is and its basic concepts, let’s dive into installing and configuring Helm in more detail.
While we covered the basic installation commands in the prerequisites section, here we’ll explore the installation process in more depth, including post-installation configuration.
After installing Helm, there are a few configuration steps you might want to perform:
Repository Setup:
# Add the popular Bitnami repository
helm repo add bitnami https://charts.bitnami.com/bitnami
# Add the stable repository
helm repo add stable https://charts.helm.sh/stable
# Update repositories
helm repo update
Outcome: Configures Helm with commonly used repositories, giving you access to hundreds of pre-built charts.
Verify Configuration:
# List configured repositories
helm repo list
# Check available charts
helm search repo
Outcome: Confirms repositories are correctly configured and shows available charts.
Helm stores its configuration in specific locations that you can customize:
~/.config/helm/
and ~/.helm/
%USERPROFILE%\.helm\
View your Helm configuration:
ls -la ~/.config/helm/
Outcome: Shows all configuration files and directories managed by Helm.
Helm’s behavior can be customized using environment variables:
export HELM_DRIVER=configmap
Outcome: Helm stores release information in Kubernetes ConfigMaps instead of Secrets.
Helm’s functionality can be extended with plugins:
# Install the popular diff plugin
helm plugin install https://github.com/databus23/helm-diff
# Install the secrets plugin
helm plugin install https://github.com/jkroepke/helm-secrets
Outcome: Adds new functionality to Helm:
If you’re behind a corporate proxy:
# Set HTTP proxy
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
Outcome: Helm can communicate through the corporate proxy.
When using Helm in a production environment with RBAC enabled:
Create a service account for Helm:
kubectl create serviceaccount helm --namespace kube-system
Create a cluster role binding:
kubectl create clusterrolebinding helm --clusterrole=cluster-admin --serviceaccount=kube-system:helm
Outcome: Provides Helm with the necessary permissions to manage resources in your cluster.
For handling sensitive data (passwords, keys, etc.):
Install the helm-secrets plugin:
helm plugin install https://github.com/jkroepke/helm-secrets
Create encrypted values file:
helm secrets enc secrets.yaml
Use in deployment:
helm install myapp ./my-chart -f secrets.yaml --set someValue=123
Outcome: Sensitive information is encrypted in your repository and decrypted only during deployment.
When a new version of Helm is released:
# For macOS
brew upgrade helm
# For Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# For Windows
# Download new version and replace binary
Outcome: Updates Helm to the latest version.
helm repo update
Following these best practices ensures a smooth Helm experience across your team and environments.
After installing and configuring Helm, it’s time to start using it. This section walks you through the essential tasks when working with Helm for the first time.
Before installing applications, you need to know what’s available:
# Search for available charts
helm search repo
# Search for a specific application
helm search repo mysql
Outcome: Displays available charts, helping you discover applications to install.
Before installing, you may want to learn more about a specific chart:
# Show chart details
helm show chart bitnami/mysql
# Show configurable values
helm show values bitnami/mysql
Outcome: Provides detailed information about the chart and configurable parameters.
Let’s install a simple application using Helm. We’ll use the popular MySQL database as an example:
# Install MySQL with a release name of "my-database"
helm install my-database bitnami/mysql
After running this command, Helm will output:
Outcome: MySQL is deployed to your Kubernetes cluster with default configuration values.
Right after installation, verify the status:
# List all releases
helm list
# Check detailed status
helm status my-database
What you’ll see:
The output from helm install
and helm status
typically includes instructions for accessing the deployed application. For MySQL, it might look like:
# Get the MySQL root password
kubectl get secret --namespace default my-database-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode
# Connect to MySQL using port-forwarding
kubectl port-forward --namespace default svc/my-database-mysql 3306:3306
Outcome: You can access and use the MySQL database you just deployed.
The real power of Helm comes with customization. Let’s see how to install an application with custom values:
--set
FlagFor simple customizations, use the --set
flag:
helm install my-database bitnami/mysql \
--set auth.rootPassword=mypassword \
--set primary.persistence.size=10Gi
Outcome: Installs MySQL with a custom root password and a 10GB persistent volume.
For more complex configurations, create a values file:
Create a file named my-values.yaml
:
auth:
rootPassword: mypassword
database: myapp
username: myuser
password: userpassword
primary:
persistence:
size: 10Gi
secondary:
replicaCount: 2
Install using this file:
helm install my-database bitnami/mysql -f my-values.yaml
Outcome: Installs MySQL with multiple customizations, including creating an initial database and user.
After installation, you’ll need to manage your application throughout its lifecycle:
When you want to change configuration or update to a newer version:
# Update repositories first
helm repo update
# Upgrade with new values
helm upgrade my-database bitnami/mysql --set primary.persistence.size=20Gi
Outcome: MySQL is upgraded with the new configuration (20GB storage) while preserving data.
If an upgrade doesn’t work as expected:
# List revision history
helm history my-database
# Roll back to a previous revision
helm rollback my-database 1
Outcome: Reverts the application to revision 1 (the initial installation).
When you no longer need the application:
helm uninstall my-database
Outcome: Removes all Kubernetes resources created by the chart.
Sometimes installations don’t go as planned. Here’s how to troubleshoot:
Test an installation without actually installing:
helm install my-database bitnami/mysql --dry-run
Outcome: Shows what resources would be created without actually creating them.
If you suspect template rendering issues:
helm template my-database bitnami/mysql --debug
Outcome: Displays the rendered YAML files for inspection.
If deployment fails:
kubectl get events --sort-by='.lastTimestamp'
Outcome: Shows Kubernetes events that might indicate why the deployment failed.
Once you’re comfortable using Helm, you might want to create your own chart:
# Create a new chart scaffold
helm create my-first-chart
This creates a directory with the following structure:
my-first-chart/
Chart.yaml
values.yaml
templates/
charts/
.helmignore
What each file does:
values.yaml
to set default valuestemplates/
directoryChart.yaml
with appropriate metadataValidate your chart before deploying:
# Lint your chart
helm lint my-first-chart
# Test template rendering
helm template my-release my-first-chart
Outcome: Identifies potential issues before deployment.
helm install my-release ./my-first-chart
Outcome: Deploys your application using your custom chart.
helm list
--namespace
By following these practices, you’ll develop good habits while learning Helm.
As you become more comfortable with Helm’s basic functionality, you’ll want to explore its advanced features. This section covers powerful capabilities that will help you tackle complex deployment scenarios.
Many applications require other services to function properly. For example, a web application might need:
Helm allows you to define these dependencies in your chart.
In your chart’s Chart.yaml
file:
dependencies:
- name: mysql
version: 8.8.x
repository: https://charts.bitnami.com/bitnami
condition: mysql.enabled
- name: redis
version: 16.x.x
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
What this does:
After defining dependencies:
# Update dependencies
helm dependency update ./my-chart
# Build dependencies (download and unpack charts)
helm dependency build ./my-chart
Outcome: Downloads and packages dependency charts in the charts/
directory.
You can set values for dependencies in your main values.yaml
file:
mysql:
enabled: true
auth:
rootPassword: mypassword
database: myapp
redis:
enabled: true
auth:
password: redispassword
Outcome: The dependency charts inherit these values when deployed.
Hooks allow you to intervene at specific points in a release’s lifecycle.
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-migration
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "0"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: migrations
image: {{ .Values.migrationImage }}
command: ["./migrate.sh"]
restartPolicy: Never
What this does:
Tests help verify that your deployed application works correctly.
In your chart’s templates/tests/
directory, create test resources:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ["wget"]
args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
What this does: Creates a pod that tests connectivity to your application.
After installing your chart:
helm test my-release
Outcome: Executes all test pods and reports success or failure.
Library charts provide reusable templates that can be shared across multiple charts.
Create a chart with type: library
in Chart.yaml
:
apiVersion: v2
name: my-library
type: library
version: 0.1.0
Create helper templates in the templates/
directory:
# templates/_helpers.tpl
{{- define "mylibrary.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end -}}
Add the library as a dependency:
dependencies:
- name: my-library
version: 0.1.0
repository: https://my-repo.example.com/
Use the templates in your chart:
metadata:
labels: { { - include "mylibrary.labels" . | nindent 4 } }
Outcome: Promotes reuse and consistency across charts.
There are two ways to include other charts in your chart:
charts/
directoryChart.yaml
Helm templates support conditional logic and iteration:
# Conditional
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Release.Name }}-sa
{{- end }}
# Iteration
{{- range .Values.configFiles }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ $.Release.Name }}-{{ .name }}
data:
{{ .filename }}: |-
{{ .content }}
{{- end }}
Outcome: Creates resources dynamically based on configuration.
Create reusable template blocks:
# Define a template
{{- define "mychart.labels" -}}
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
{{- end -}}
# Use the template
metadata:
labels:
{{- include "mychart.labels" . | nindent 4 }}
Outcome: Promotes DRY (Don’t Repeat Yourself) principles.
Helm provides many built-in functions for advanced templating:
# String manipulation
app: { { .Values.name | lower | trunc 63 | trimSuffix "-" } }
# Data transformation
annotations:
checksum/config:
{ { include (print $.Template.BasePath "/configmap.yaml") . | sha256sum } }
# Default values
replicas: { { .Values.replicas | default 1 } }
Outcome: Enables sophisticated template logic.
Helm supports OCI (Open Container Initiative) registries for chart storage.
# Package the chart
helm package ./my-chart
# Push to registry
helm push my-chart-0.1.0.tgz oci://registry.example.com/charts
Outcome: Stores the chart in a container registry.
helm install my-release oci://registry.example.com/charts/my-chart --version 0.1.0
Outcome: Installs the chart directly from the OCI registry.
# Generate a signing key
gpg --gen-key
# Sign the chart
helm package --sign --key 'John Doe' ./my-chart
# Verify a signed chart
helm verify my-chart-0.1.0.tgz
Outcome: Ensures chart integrity and authenticity.
When you sign a chart, Helm creates a .prov
file:
my-chart-0.1.0.tgz.prov
This file contains:
Starters are chart templates that serve as a starting point for new charts.
$XDG_DATA_HOME/helm/starters/
or ~/.helm/starters/
helm create my-new-chart --starter=my-starter
Outcome: Creates a new chart based on your starter template.
Many Kubernetes extensions use CRDs. Helm provides ways to manage them:
Place CRDs in the crds/
directory of your chart:
my-chart/
crds/
mycrd.yaml
templates/
deployment.yaml
Important notes:
crds/
directory are installed before other resourcesCreate separate value files for each environment:
my-chart/
values.yaml # Default values
values-dev.yaml # Development values
values-staging.yaml # Staging values
values-prod.yaml # Production values
# Development
helm install my-app ./my-chart -f values-dev.yaml
# Production
helm install my-app ./my-chart -f values-prod.yaml
Outcome: Consistent chart with environment-specific configurations.
Post-rendering allows you to modify rendered manifests before they’re applied to the cluster.
helm install my-release ./my-chart --post-renderer ./my-post-renderer
Where my-post-renderer
is an executable that:
Use cases:
By mastering these advanced features, you’ll be able to handle complex deployment scenarios with elegance and efficiency.
Creating effective Helm charts requires more than just technical knowledge—it demands an understanding of best practices. This section will guide you through creating robust charts and following industry-standard practices.
A well-organized chart structure is crucial for maintainability. Let’s explore the complete structure of a production-ready chart:
mychart/
├── .helmignore # Files to ignore when packaging
├── Chart.yaml # Chart metadata
├── values.yaml # Default configuration values
├── values.schema.json # JSON schema for validating values
├── README.md # Documentation
├── LICENSE # License information
├── crds/ # Custom Resource Definitions
├── templates/ # Template files
│ ├── NOTES.txt # Usage notes displayed after installation
│ ├── _helpers.tpl # Template helpers
│ ├── deployment.yaml # Kubernetes Deployment
│ ├── service.yaml # Kubernetes Service
│ ├── ingress.yaml # Kubernetes Ingress
│ ├── configmap.yaml # Kubernetes ConfigMap
│ ├── secret.yaml # Kubernetes Secret
│ └── tests/ # Test files
│ └── test-connection.yaml
└── charts/ # Dependency charts
Chart.yaml: Contains metadata about your chart:
apiVersion: v2
name: my-application
version: 1.0.0
description: A Helm chart for my application
type: application
appVersion: "2.3.4"
dependencies:
- name: postgresql
version: 11.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
values.yaml: Defines default configuration values:
replicaCount: 2
image:
repository: nginx
tag: 1.21
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
templates/NOTES.txt: Instructions displayed after installation:
Thank you for installing {{ .Chart.Name }}.
Your release is named {{ .Release.Name }}.
To get the application URL, run:
{{- if .Values.ingress.enabled }}
http{{ if .Values.ingress.tls }}s{{ end }}://{{ .Values.ingress.hostname }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services {{ .Release.Name }})
export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
export SERVICE_IP=$(kubectl get svc {{ .Release.Name }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else }}
kubectl port-forward svc/{{ .Release.Name }} {{ .Values.service.port }}:{{ .Values.service.port }}
echo "Visit http://localhost:{{ .Values.service.port }}"
{{- end }}
Your chart should work with minimal configuration while allowing for customization:
# Good default values
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
Outcome: Users can deploy quickly with sensible defaults or override them as needed.
Security should never be an afterthought:
# Security-focused defaults
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
podSecurityContext:
fsGroup: 1000
Outcome: Pods run with least privilege by default.
Design templates to handle various deployment scenarios:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
{{- with .Values.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.replicaCount }}
{{- if .Values.autoscaling.enabled }}
# Placeholder for autoscaling configuration
{{- end }}
Outcome: Templates adapt to different requirements without needing modification.
Document all configurable values in values.yaml
with clear comments:
# Number of application replicas
replicaCount: 2
image:
# Repository where the image is stored
repository: nginx
# Image tag to use
tag: 1.21
# Image pull policy
pullPolicy: IfNotPresent
Outcome: Users understand how to configure the chart without reading the code.
Lint Tests:
helm lint ./mychart
Outcome: Validates chart syntax and best practices.
Template Tests:
helm template ./mychart
Outcome: Renders all templates without installing.
Unit Tests (using tools like helm-unittest):
suite: test deployment
templates:
- deployment.yaml
tests:
- it: should set the correct image
set:
image.repository: nginx
image.tag: 1.21
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: nginx:1.21
Outcome: Validates specific aspects of the rendered templates.
Integration Tests:
helm install test-release ./mychart --dry-run
Outcome: Verifies the chart can be installed without errors.
Chart Tests:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "mychart.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
Outcome: Tests actual functionality after deployment.
Follow semantic versioning (SemVer) for your charts:
0.1.0
during development1.0.0
when ready for production1.1.0
when adding new features1.1.1
when fixing bugs2.0.0
when making breaking changesOutcome: Users understand the impact of chart updates.
A good README.md
should include:
## Parameters
### Global parameters
| Name | Description | Value |
| ---------------------- | -------------------------------------------- | ----- |
| `global.imageRegistry` | Global Docker image registry | `""` |
| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` |
### Common parameters
| Name | Description | Value |
| ------------------ | ------------------ | -------------- |
| `replicaCount` | Number of replicas | `1` |
| `image.repository` | Image repository | `nginx` |
| `image.tag` | Image tag | `1.21` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
## Upgrading
### To 2.0.0
This version includes breaking changes:
- The configuration parameter `foo` has been renamed to `bar`
- Minimum Kubernetes version is now 1.19
Even with careful planning, you may encounter issues when working with Helm. This section provides strategies for diagnosing and resolving common Helm problems, with practical examples and solutions.
Error message:
Error: chart "my-chart" not found in local repository
Possible causes:
Solutions:
Verify the chart exists:
helm search repo my-chart
Add missing repository:
helm repo add bitnami https://charts.bitnami.com/bitnami
Update repositories:
helm repo update
Error message:
Error: Failed to download chart from repo
Possible causes:
Solutions:
Check network connection:
curl -I https://charts.bitnami.com/bitnami
Configure proxy settings:
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
Use a different repository mirror if available.
Error message:
Error: INSTALLATION FAILED: failed to create: secrets is forbidden: User "system:serviceaccount:kube-system:default" cannot create resource "secrets"
Possible causes:
Solutions:
Create proper service account and role binding:
kubectl create serviceaccount tiller --namespace kube-system
kubectl create clusterrolebinding tiller-cluster-rule \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:tiller
Specify a different namespace with proper permissions:
helm install my-release bitnami/mysql --namespace my-namespace
Use --debug
flag to get more detailed error information:
helm install my-release bitnami/mysql --debug
Error message: No direct error, but pods remain in Pending
state.
Possible causes:
Solutions:
Check pod events:
kubectl describe pod my-release-mysql-0
Verify available resources:
kubectl get nodes -o wide
kubectl describe nodes
Check PVC status:
kubectl get pvc
kubectl describe pvc my-release-mysql
Modify resource requests in values.yaml
:
resources:
requests:
memory: "512Mi"
cpu: "200m"
Error message: Pods repeatedly crash and restart.
Possible causes:
Solutions:
Check container logs:
kubectl logs my-release-nginx-58d4f8ff58-xmz9f
Debug with an interactive shell:
kubectl exec -it my-release-nginx-58d4f8ff58-xmz9f -- /bin/bash
Override problematic values during installation:
helm install my-release bitnami/mysql --set auth.rootPassword=my-password
Use --set
to troubleshoot by modifying configuration:
helm upgrade my-release bitnami/mysql --set readinessProbe.enabled=false
Error message: No direct error, but service isn’t accessible.
Possible causes:
Solutions:
Verify service configuration:
kubectl describe service my-release-nginx
Check service endpoints:
kubectl get endpoints my-release-nginx
Test connectivity from within the cluster:
kubectl run -it --rm debug --image=busybox --restart=Never -- wget -O- my-release-nginx
Check pod labels and service selectors:
kubectl get pods --show-labels
Error message:
Error: parse error at (mychart/templates/deployment.yaml:15): function "Values" not defined
Possible causes:
Solutions:
Test template rendering:
helm template ./mychart
Check specific template file:
helm template ./mychart -s templates/deployment.yaml
Add --debug
for more information:
helm template ./mychart --debug
Fix common syntax issues:
.Values
to .Values.someKey
{{ }}
vs { { } }
Error message:
Error: error converting YAML to JSON: yaml: line 10: mapping values are not allowed in this context
Possible causes:
Solutions:
Validate YAML syntax:
helm lint ./mychart
Inspect rendered templates:
helm template ./mychart > rendered.yaml
yamllint rendered.yaml
Fix indentation issues in template files:
# Correct
spec:
containers:
- name: {{ .Values.name }}
# Incorrect
spec:
containers:
- name: {{ .Values.name }}
Error message:
Error: execution error at (mychart/templates/deployment.yaml:25:17): nil pointer evaluating interface {}.image.repository
Possible causes:
values.yaml
Solutions:
Use the default
function:
image: "{{ .Values.image.repository | default "nginx" }}:{{ .Values.image.tag | default "latest" }}"
Use conditional checks:
{{- if .Values.image -}}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
{{- else -}}
image: "nginx:latest"
{{- end -}}
Check values passed to template:
helm install --dry-run --debug ./mychart
Error message:
Error: UPGRADE FAILED: cannot patch "my-release-mysql" with kind StatefulSet: StatefulSet.apps "my-release-mysql" is invalid
Possible causes:
Solutions:
Check the chart’s upgrade notes:
helm show readme bitnami/mysql
Use --force
for certain upgrades:
helm upgrade my-release bitnami/mysql --force
Uninstall and reinstall if necessary (data may be lost):
helm uninstall my-release
helm install my-release bitnami/mysql
Use the --dry-run
flag to test upgrade:
helm upgrade --dry-run my-release bitnami/mysql
Error message:
Error: ROLLBACK FAILED: failed to replace object
Possible causes:
Solutions:
Check revision history:
helm history my-release
Force a reinstall:
helm upgrade --force my-release bitnami/mysql --version 8.8.8
Check for external modifications:
kubectl describe deployment my-release-mysql
Use --recreate-pods
for certain updates:
helm upgrade my-release bitnami/mysql --recreate-pods
Error message:
Error: failed to download "bitnami/mysql" (hint: running `helm repo update` may help)
Possible causes:
Solutions:
List available versions:
helm search repo bitnami/mysql --versions
Specify a compatible version:
helm install my-release bitnami/mysql --version 8.8.26
Update chart dependencies:
helm dependency update ./mychart
Check Kubernetes version compatibility:
kubectl version
View all releases:
helm list --all-namespaces
Examine release history:
helm history my-release
See release content:
helm get all my-release
View manifests:
helm get manifest my-release
Check release values:
helm get values my-release
Extract release secrets manually:
kubectl get secret sh.helm.release.v1.my-release.v1 -o jsonpath="{.data.release}" | base64 -d | base64 -d | gunzip -c
Installation with debug:
helm install my-release bitnami/mysql --debug
Set Helm log level:
export HELM_DEBUG=true
Debug template rendering:
helm template --debug ./mychart
Extract and modify chart locally:
helm pull bitnami/mysql --untar
# Make changes to the chart
helm install my-release ./mysql
Disable hooks for troubleshooting:
helm upgrade my-release bitnami/mysql --no-hooks
Skip CRD installation:
helm upgrade my-release bitnami/mysql --skip-crds
Perform server-side template rendering:
helm upgrade my-release bitnami/mysql --debug
Symptoms: Helm commands take a long time to complete.
Possible causes:
Solutions:
Limit history:
helm install my-release bitnami/mysql --history-max 3
Clean up old releases:
helm list --all --all-namespaces | grep FAILED | awk '{print $1, $2}' | xargs -L1 helm uninstall -n
Use specific namespace scope:
helm list -n my-namespace
Optimize hook usage in chart templates.
Error message:
Error: command failed with exit code: 137
Possible causes:
Solutions:
Increase container memory if running in container.
Split large charts into smaller ones:
# Instead of one monolith chart
helm install my-app ./monolith
# Use multiple smaller charts
helm install my-app-frontend ./frontend
helm install my-app-backend ./backend
helm install my-app-db ./database
Use --create-namespace
instead of templates for namespace creation.
Create a troubleshooting script with common commands:
#!/bin/bash
# helm-diagnose.sh
RELEASE=$1
NAMESPACE=$2
echo "Checking Helm release..."
helm status $RELEASE -n $NAMESPACE
helm get all $RELEASE -n $NAMESPACE
echo "Checking Kubernetes resources..."
kubectl get all -n $NAMESPACE -l app.kubernetes.io/instance=$RELEASE
kubectl describe pods -n $NAMESPACE -l app.kubernetes.io/instance=$RELEASE
kubectl logs -n $NAMESPACE -l app.kubernetes.io/instance=$RELEASE --tail=100
echo "Checking for events..."
kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' | grep $RELEASE
Usage:
./helm-diagnose.sh my-release my-namespace
Outcome: Quick overview of the release’s health and potential issues.
Prometheus alerts for Helm releases:
groups:
- name: helm-alerts
rules:
- alert: HelmReleaseNotReady
expr: time() - max(kube_pod_created_time{pod=~"my-release.*"}) > 300 and sum(kube_pod_status_phase{phase="Running", pod=~"my-release.*"}) < 1
for: 5m
labels:
severity: warning
annotations:
summary: "Helm release pods not ready"
description: "Pods for release my-release have not become ready within 5 minutes."
Outcome: Automated alerts when releases have issues.
When encountering Helm issues, follow this checklist:
Basic Verification:
helm version
)kubectl config current-context
)helm repo update
)Chart Investigation:
helm search repo chart --versions
)helm show values chart
)Kubernetes Validation:
kubectl get crds
)kubectl auth can-i create deployments
)kubectl top nodes
)Deployment Analysis:
kubectl get pods -l release=my-release
)kubectl logs deployment/my-release
)kubectl get events
)Advanced Troubleshooting:
helm template ./chart
)helm install --debug --dry-run
)kubectl get secrets -l owner=helm
)By methodically working through this guide, you’ll be equipped to diagnose and resolve most Helm and Kubernetes deployment issues you encounter.
Throughout this comprehensive guide, we’ve explored Helm, the powerful package manager for Kubernetes that simplifies deploying and managing applications. Let’s recap what we’ve learned and discuss where you might go next in your Helm journey.
Helm addresses several pain points in Kubernetes application management:
The Helm ecosystem offers:
Following best practices ensures successful Helm usage:
Even with Helm’s simplifications, troubleshooting skills remain crucial:
Organizations adopting Helm typically experience:
According to the CNCF Survey, Helm is one of the most widely adopted Kubernetes tools, with over 63% of organizations using it in production.
To deepen your Helm expertise:
As you become more proficient, consider these advanced areas:
Helm has transformed how organizations deploy and manage applications on Kubernetes. By abstracting away complexity while providing powerful customization options, it strikes a balance that benefits both beginners and experts.
As Kubernetes continues to evolve, Helm will likely remain a critical tool in the ecosystem. The skills you’ve learned in this guide provide a solid foundation for navigating the cloud-native landscape and efficiently managing applications at scale.
Remember that mastering Helm is a journey. Start with simple charts, gradually incorporate advanced features, and continuously refine your approach based on your organization’s needs and experiences.
By embracing Helm as part of your Kubernetes toolkit, you’re well-positioned to tackle the challenges of modern application deployment with confidence and efficiency.