Helm Charts
Package and deploy Kubernetes applications using Helm, the package manager for Kubernetes.
When to Use This Skill
Use this skill when:
-
Creating reusable Kubernetes application packages
-
Deploying applications with configurable values
-
Managing Helm releases and upgrades
-
Using third-party Helm charts
-
Implementing chart versioning and repositories
Prerequisites
-
Helm 3.x installed
-
kubectl configured with cluster access
-
Basic Kubernetes knowledge
Chart Structure
mychart/ ├── Chart.yaml # Chart metadata ├── values.yaml # Default configuration values ├── charts/ # Chart dependencies ├── templates/ # Kubernetes manifest templates │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── configmap.yaml │ ├── secret.yaml │ ├── _helpers.tpl # Template helpers │ ├── NOTES.txt # Post-install notes │ └── tests/ │ └── test-connection.yaml └── .helmignore # Files to ignore
Chart.yaml
apiVersion: v2 name: myapp description: A Helm chart for MyApp type: application version: 1.0.0 appVersion: "2.0.0" keywords:
- myapp
- web maintainers:
- name: DevOps Team email: devops@example.com dependencies:
- name: postgresql version: "12.x.x" repository: "https://charts.bitnami.com/bitnami" condition: postgresql.enabled
values.yaml
replicaCount: 2
image: repository: myapp tag: "" # Defaults to appVersion pullPolicy: IfNotPresent
service: type: ClusterIP port: 80
ingress: enabled: false className: nginx hosts: - host: myapp.example.com paths: - path: / pathType: Prefix tls: []
resources: limits: cpu: 500m memory: 256Mi requests: cpu: 100m memory: 128Mi
postgresql: enabled: true auth: database: myapp
Templates
Deployment Template
templates/deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "myapp.selectorLabels" . | nindent 8 }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 8080 {{- with .Values.resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} env: - name: DATABASE_URL valueFrom: secretKeyRef: name: {{ include "myapp.fullname" . }}-secrets key: database-url
Helper Functions
templates/_helpers.tpl
{{/* Expand the name of the chart. */}} {{- define "myapp.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }}
{{/* Create a default fully qualified app name. */}} {{- define "myapp.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }}
{{/* Common labels */}} {{- define "myapp.labels" -}} helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} {{ include "myapp.selectorLabels" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }}
{{/* Selector labels */}} {{- define "myapp.selectorLabels" -}} app.kubernetes.io/name: {{ include "myapp.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }}
Conditional Resources
templates/ingress.yaml
{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: ingressClassName: {{ .Values.ingress.className }} {{- if .Values.ingress.tls }} tls: {{- range .Values.ingress.tls }} - hosts: {{- range .hosts }} - {{ . | quote }} {{- end }} secretName: {{ .secretName }} {{- end }} {{- end }} rules: {{- range .Values.ingress.hosts }} - host: {{ .host | quote }} http: paths: {{- range .paths }} - path: {{ .path }} pathType: {{ .pathType }} backend: service: name: {{ include "myapp.fullname" $ }} port: number: {{ $.Values.service.port }} {{- end }} {{- end }} {{- end }}
Helm Commands
Installing Charts
Install from local chart
helm install myapp ./mychart
Install with custom values
helm install myapp ./mychart -f custom-values.yaml
Install with value overrides
helm install myapp ./mychart
--set replicaCount=3
--set image.tag=2.0.0
Install in specific namespace
helm install myapp ./mychart -n production --create-namespace
Dry run to preview
helm install myapp ./mychart --dry-run --debug
Managing Releases
List releases
helm list helm list -A # All namespaces
Upgrade release
helm upgrade myapp ./mychart helm upgrade myapp ./mychart -f new-values.yaml
Rollback
helm rollback myapp 1 helm history myapp
Uninstall
helm uninstall myapp
Chart Development
Create new chart
helm create mychart
Lint chart
helm lint ./mychart
Template locally (debug)
helm template myapp ./mychart
Package chart
helm package ./mychart
Update dependencies
helm dependency update ./mychart
Repositories
Add repository
helm repo add bitnami https://charts.bitnami.com/bitnami
Update repositories
helm repo update
Search charts
helm search repo postgresql helm search hub prometheus
Install from repo
helm install postgres bitnami/postgresql
Show chart info
helm show values bitnami/postgresql
Advanced Features
Hooks
templates/pre-install-job.yaml
apiVersion: batch/v1 kind: Job metadata: name: {{ include "myapp.fullname" . }}-migration annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "-5" "helm.sh/hook-delete-policy": hook-succeeded spec: template: spec: containers: - name: migrate image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" command: ["./migrate.sh"] restartPolicy: Never
Tests
templates/tests/test-connection.yaml
apiVersion: v1 kind: Pod metadata: name: "{{ include "myapp.fullname" . }}-test-connection" annotations: "helm.sh/hook": test spec: containers: - name: wget image: busybox command: ['wget'] args: ['{{ include "myapp.fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never
Run tests
helm test myapp
Library Charts
Chart.yaml
apiVersion: v2 name: mylib type: library version: 1.0.0
Using library chart
dependencies:
- name: mylib version: "1.x.x" repository: "file://../mylib"
OCI Registry Support
Login to registry
helm registry login registry.example.com
Push chart to OCI registry
helm push mychart-1.0.0.tgz oci://registry.example.com/charts
Pull from OCI registry
helm pull oci://registry.example.com/charts/mychart --version 1.0.0
Install from OCI
helm install myapp oci://registry.example.com/charts/mychart
Common Issues
Issue: YAML Indentation Errors
Problem: Template renders with wrong indentation Solution: Use nindent helper function
{{- toYaml .Values.resources | nindent 12 }}
Issue: Values Not Applying
Problem: Custom values not reflected Solution: Check value paths, use --debug flag
helm template myapp ./mychart --debug
Issue: Dependency Errors
Problem: Chart dependencies not found Solution: Run helm dependency update
Issue: Release Already Exists
Problem: Cannot install, release exists Solution: Use helm upgrade --install
helm upgrade --install myapp ./mychart
Best Practices
-
Use semantic versioning for charts
-
Provide comprehensive default values
-
Document all values in values.yaml with comments
-
Use helper templates for repeated patterns
-
Implement chart tests
-
Use .helmignore to exclude unnecessary files
-
Pin dependency versions
-
Use helm lint in CI pipelines
Related Skills
-
kubernetes-ops - K8s fundamentals
-
argocd-gitops - GitOps with Helm
-
kustomize - Alternative templating