helm · git:20260313.3ab01ea · 2026-03-13 · sha256 528fc4ab54b81e9f
helm git:20260313.3ab01eaA
Immutable. This exact content is served forever at /api/v1/blob/528fc4ab54b81e9f.
---
name: helm-charts
description: Helm package manager for Kubernetes. Use when creating, managing, or debugging Helm charts, writing Chart.yaml or values.yaml files, templating K8s manifests with Go templates, managing Helm releases (install, upgrade, rollback), working with Helm repositories, creating chart libraries, or troubleshooting Helm deployment issues. Covers chart structure, values overrides, hooks, tests, dependencies, and OCI registry usage.
---
# Helm Package Manager
Comprehensive guidance for managing Kubernetes applications with Helm charts, from chart creation to production deployment patterns.
## Quick Start
### Basic operations
```bash
# Install a release
helm install my-release ./chart -n namespace
# Upgrade a release
helm upgrade my-release ./chart -n namespace -f values.yaml
# Rollback to previous revision
helm rollback my-release 1 -n namespace
# Uninstall a release
helm uninstall my-release -n namespace
# List releases
helm list -n namespace
helm list -A # All namespaces
```
### Repository management
```bash
# Add repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# Search for charts
helm search repo nginx
helm search hub prometheus # Search Artifact Hub
```
## Chart Structure
### Standard layout
```
mychart/
├── Chart.yaml # Chart metadata (name, version, dependencies)
├── Chart.lock # Locked dependency versions
├── values.yaml # Default configuration values
├── values.schema.json # Optional JSON schema for values validation
├── templates/
│ ├── _helpers.tpl # Template helpers (named templates)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── NOTES.txt # Post-install usage instructions
│ └── tests/
│ └── test-connection.yaml
├── charts/ # Dependency charts (vendored)
└── .helmignore # Files to exclude from packaging
```
### Chart.yaml
```yaml
apiVersion: v2
name: my-app
description: A Helm chart for my application
type: application # or "library"
version: 1.2.0 # Chart version (SemVer)
appVersion: "3.1.0" # Application version
keywords:
- app
- backend
home: https://github.com/org/my-app
sources:
- https://github.com/org/my-app
maintainers:
- name: Team Name
email: team@example.com
dependencies:
- name: postgresql
version: "~13.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: redis
version: "~18.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
```
### values.yaml
```yaml
# Default values for my-app
replicaCount: 2
image:
repository: gcr.io/my-project/my-app
tag: "" # Defaults to appVersion
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: false
className: traefik
annotations: {}
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls: []
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
env: []
envFrom: []
configMap:
enabled: false
data: {}
secret:
enabled: false
data: {}
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
nodeSelector: {}
tolerations: []
affinity: {}
postgresql:
enabled: false
redis:
enabled: false
```
## Go Templating
### Template helpers (_helpers.tpl)
```yaml
{{/*
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 }}
{{/*
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 }}
{{/*
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 }}
{{/*
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 }}
```
### Common template patterns
```yaml
# Conditional blocks
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
# ...
{{- end }}
# Range (loops)
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
# With (scope change)
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
# Default values
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
# Include and indent
{{- include "mychart.labels" . | nindent 4 }}
# toYaml for passing complex structures
resources:
{{- toYaml .Values.resources | nindent 12 }}
# Ternary-like
replicas: {{ ternary 1 .Values.replicaCount .Values.autoscaling.enabled }}
# Required values
{{ required "image.repository is required" .Values.image.repository }}
```
### Deployment template
```yaml
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:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
{{- include "mychart.labels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "mychart.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
```
## Values Management
### Override strategies
```bash
# Single values file
helm install my-release ./chart -f values.yaml
# Multiple values files (later files override earlier)
helm install my-release ./chart \
-f values.yaml \
-f values-prod.yaml
# Inline overrides (highest priority)
helm install my-release ./chart \
-f values.yaml \
--set image.tag=v1.2.3 \
--set replicaCount=3
# Set string values
helm install my-release ./chart --set-string annotations."key"="value"
# Set from file
helm install my-release ./chart --set-file config=./app-config.json
```
### Environment-specific values pattern
```
chart/
├── values.yaml # Defaults
├── values-dev.yaml # Dev overrides
├── values-staging.yaml # Staging overrides
└── values-prod.yaml # Production overrides
```
```bash
# Deploy to prod
helm upgrade --install my-release ./chart \
-f values.yaml \
-f values-prod.yaml \
-n production
```
### values.schema.json (validation)
```json
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["image", "service"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1
},
"image": {
"type": "object",
"required": ["repository"],
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" },
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
}
}
}
}
```
## Hooks
### Pre/post install and upgrade hooks
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "mychart.fullname" . }}-db-migrate
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-5" # Lower runs first
"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 | default .Chart.AppVersion }}"
command: ["python", "manage.py", "migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
backoffLimit: 1
```
### Hook types
| Hook | Description |
|------|-------------|
| `pre-install` | Before any resources are installed |
| `post-install` | After all resources are installed |
| `pre-upgrade` | Before any resources are upgraded |
| `post-upgrade` | After all resources are upgraded |
| `pre-delete` | Before any resources are deleted |
| `post-delete` | After all resources are deleted |
| `pre-rollback` | Before rollback |
| `post-rollback` | After rollback |
| `test` | When `helm test` is invoked |
### Delete policies
- `before-hook-creation` — delete previous hook resource before new one is created
- `hook-succeeded` — delete after hook succeeds
- `hook-failed` — delete after hook fails
## Chart Tests
```yaml
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "mychart.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
restartPolicy: Never
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}/healthz']
```
```bash
# Run tests
helm test my-release -n namespace
# Run tests with logs
helm test my-release -n namespace --logs
```
## Dependencies
### Managing chart dependencies
```bash
# Download dependencies
helm dependency update ./chart
# List dependencies
helm dependency list ./chart
# Build (rebuild charts/ directory)
helm dependency build ./chart
```
### Conditional dependencies
```yaml
# Chart.yaml
dependencies:
- name: postgresql
version: "~13.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
tags:
- database
```
### Overriding dependency values
```yaml
# values.yaml — override sub-chart values under their name
postgresql:
enabled: true
auth:
postgresPassword: secret
database: myapp
primary:
persistence:
size: 10Gi
```
## OCI Registry
### Push and pull charts via OCI
```bash
# Login to registry
helm registry login gcr.io -u _json_key --password-stdin < key.json
# Package chart
helm package ./chart
# Push to OCI registry
helm push my-app-1.2.0.tgz oci://gcr.io/my-project/charts
# Pull from OCI registry
helm pull oci://gcr.io/my-project/charts/my-app --version 1.2.0
# Install from OCI
helm install my-release oci://gcr.io/my-project/charts/my-app --version 1.2.0
```
## Library Charts
### Creating a library chart
```yaml
# Chart.yaml
apiVersion: v2
name: common-lib
type: library
version: 1.0.0
```
```yaml
# templates/_deployment.tpl
{{- define "common-lib.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "common-lib.fullname" . }}
labels:
{{- include "common-lib.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "common-lib.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "common-lib.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- end }}
```
### Using a library chart
```yaml
# Chart.yaml of consuming chart
dependencies:
- name: common-lib
version: "1.0.0"
repository: "file://../common-lib"
```
```yaml
# templates/deployment.yaml
{{- include "common-lib.deployment" . }}
```
## Release Management
### Inspect releases
```bash
# Release history
helm history my-release -n namespace
# Show current values
helm get values my-release -n namespace
helm get values my-release -n namespace -a # Including defaults
# Show rendered manifests
helm get manifest my-release -n namespace
# Show release notes
helm get notes my-release -n namespace
# Show all release info
helm get all my-release -n namespace
```
### Upgrade patterns
```bash
# Upgrade with atomic (auto-rollback on failure)
helm upgrade my-release ./chart -n namespace \
-f values.yaml \
--atomic \
--timeout 5m
# Upgrade or install if not exists
helm upgrade --install my-release ./chart -n namespace -f values.yaml
# Dry-run to preview changes
helm upgrade my-release ./chart -n namespace -f values.yaml --dry-run
# Diff plugin (shows what will change)
helm diff upgrade my-release ./chart -n namespace -f values.yaml
```
### Rollback
```bash
# Rollback to previous revision
helm rollback my-release -n namespace
# Rollback to specific revision
helm rollback my-release 3 -n namespace
# Rollback with wait
helm rollback my-release 3 -n namespace --wait --timeout 5m
```
## Debugging
```bash
# Template rendering (without installing)
helm template my-release ./chart -f values.yaml
# Template with debug output
helm template my-release ./chart -f values.yaml --debug
# Dry-run install (server-side validation)
helm install my-release ./chart -f values.yaml --dry-run --debug
# Lint chart
helm lint ./chart
helm lint ./chart -f values.yaml
# Show computed values
helm show values ./chart
# Show chart info
helm show chart ./chart
helm show readme ./chart
```
### Common issues
**Template rendering errors:**
```bash
# Render specific template
helm template my-release ./chart -s templates/deployment.yaml
# Check for YAML validity
helm template my-release ./chart | kubectl apply --dry-run=client -f -
```
**Release stuck in pending/failed:**
```bash
# Check release status
helm status my-release -n namespace
# Force uninstall stuck release
helm uninstall my-release -n namespace --no-hooks
# If uninstall fails, remove secrets manually
kubectl delete secret -l owner=helm,name=my-release -n namespace
```
## Best Practices
1. **Use `helm upgrade --install`** for idempotent deployments
2. **Use `--atomic`** in CI/CD to auto-rollback failed upgrades
3. **Pin chart versions** in dependencies — avoid floating versions
4. **Use `values.schema.json`** to validate values before rendering
5. **Include NOTES.txt** with post-install instructions
6. **Use named templates** in `_helpers.tpl` — avoid duplication across templates
7. **Add checksum annotations** for ConfigMaps/Secrets to trigger pod restart on config changes
8. **Test charts** with `helm lint`, `helm template`, and `helm test`
9. **Separate chart version from app version** — bump chart version for chart changes, appVersion for application changes
10. **Use `.helmignore`** to exclude CI files, tests, and docs from packaged chart
## Package Requirements
This skill requires:
- `helm` >= 3.0 — Kubernetes package manager
- `kubectl` — for cluster access and validation
- Optional: `helm-diff` plugin — for previewing upgrade changes