Helm Charts
Helm chart creation, templating, dependency management, and release lifecycle for Kubernetes
You are an expert in Helm charts for containerized application development and deployment. ## Key Points - name: postgresql - Use `helm lint` and `helm template` in CI pipelines to catch errors before deployment. - Version your chart independently from your application; bump the chart version for any template or default value change. - Keep `values.yaml` well-documented with comments explaining each parameter's purpose and accepted values. - Using `helm install` instead of `helm upgrade --install`, which fails if the release already exists; the latter is idempotent and safe for CI/CD. - Forgetting `nindent` or `indent` in templates, which produces invalid YAML that only surfaces at install time.
skilldb get containerization-skills/Helm ChartsFull skill: 220 linesHelm Charts — Containerization
You are an expert in Helm charts for containerized application development and deployment.
Overview
Helm is the package manager for Kubernetes. It bundles related manifests into versioned, parameterized charts that can be installed, upgraded, and rolled back as a single unit. Helm templates use Go's text/template engine with Sprig functions, enabling reusable and configurable Kubernetes deployments.
Core Concepts
Chart Structure
mychart/
Chart.yaml # Chart metadata and dependencies
values.yaml # Default configuration values
templates/
deployment.yaml # Templated Kubernetes manifests
service.yaml
ingress.yaml
_helpers.tpl # Reusable template partials
NOTES.txt # Post-install usage instructions
charts/ # Dependency charts
Chart.yaml
apiVersion: v2
name: web-app
description: A web application Helm chart
version: 1.2.0 # Chart version
appVersion: "3.5.1" # Application version
type: application
dependencies:
- name: postgresql
version: "13.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
values.yaml
replicaCount: 2
image:
repository: myapp/web
tag: "3.5.1"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
host: app.example.com
tls: true
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
postgresql:
enabled: true
auth:
database: appdb
Templating
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "mychart.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 8080
resources:
{{- toYaml .Values.resources | nindent 12 }}
Helper Templates
# templates/_helpers.tpl
{{- define "mychart.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "mychart.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
Implementation Patterns
Environment-Specific Values
# Base values + environment overlay
helm upgrade --install web-app ./mychart \
-f values.yaml \
-f values.production.yaml \
--namespace production
# Inline overrides
helm upgrade --install web-app ./mychart \
--set replicaCount=5 \
--set image.tag=3.5.2
Conditional Resources
# templates/ingress.yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "mychart.fullname" . }}
spec:
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "mychart.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- end }}
Release Lifecycle
# Lint and dry-run before installing
helm lint ./mychart
helm template web-app ./mychart | kubectl apply --dry-run=client -f -
# Install or upgrade
helm upgrade --install web-app ./mychart --namespace prod --wait --timeout 5m
# Rollback
helm rollback web-app 2
# Uninstall
helm uninstall web-app --namespace prod
Best Practices
- Use
helm lintandhelm templatein CI pipelines to catch errors before deployment. - Version your chart independently from your application; bump the chart version for any template or default value change.
- Keep
values.yamlwell-documented with comments explaining each parameter's purpose and accepted values.
Core Philosophy
Helm charts are the packaging format for Kubernetes, and good chart design follows the same principles as good library design: sensible defaults, clear interfaces, and minimal surprise. A well-crafted chart should work out of the box for the common case with just helm install, while exposing values that let advanced users customize every aspect of the deployment. The values.yaml file is the chart's public API, and it deserves the same documentation and stability guarantees as any other interface.
Templating power should be used with restraint. Helm's Go template engine with Sprig functions can express nearly arbitrary logic, but that does not mean it should. Charts that contain complex conditionals, nested loops, and multi-level template includes become impossible to debug when they produce invalid YAML. Keep templates as simple and flat as possible, use helper templates in _helpers.tpl for repeated patterns, and always validate output with helm template and helm lint before deploying.
Chart versioning is separate from application versioning for a reason. The chart version tracks changes to templates, default values, and Kubernetes resource structure, while appVersion tracks the application release. Bumping the chart version for every template or default change lets consumers upgrade the chart independently of the application, and it makes it possible to roll back chart changes without rolling back the application.
Anti-Patterns
-
Templating everything. Making every field in every Kubernetes manifest a configurable value creates charts with hundreds of parameters that no one understands. Template the values that genuinely vary across deployments and hardcode the rest.
-
Skipping
helm lintandhelm templatein CI. Template rendering errors only surface at install time if you do not validate earlier. Runninghelm lintand pipinghelm templatethroughkubectl apply --dry-run=clientcatches syntax errors, missing values, and invalid Kubernetes manifests before they reach a cluster. -
Using
helm installin CI/CD instead ofhelm upgrade --install.helm installfails if the release already exists, making your pipeline non-idempotent.helm upgrade --installis safe to run repeatedly: it installs on first run and upgrades thereafter. -
Undocumented values.yaml. A
values.yamlfile without comments explaining each parameter's purpose, type, and accepted values forces users to read templates to understand what they can configure. Document every value with inline comments. -
Hardcoded namespace in templates. Embedding a namespace directly in template metadata prevents the chart from being installed in different namespaces. Let the
--namespaceflag control placement and use{{ .Release.Namespace }}when namespace references are needed within templates.
Common Pitfalls
- Using
helm installinstead ofhelm upgrade --install, which fails if the release already exists; the latter is idempotent and safe for CI/CD. - Forgetting
nindentorindentin templates, which produces invalid YAML that only surfaces at install time.
Install this skill directly: skilldb add containerization-skills
Related Skills
Container Registries
Container registry setup, authentication, and image management for ECR, GCR, GHCR, and Docker Hub
Container Security
Container image scanning, runtime hardening, and security best practices for production workloads
Docker Compose
Docker Compose configuration for multi-service development, testing, and local orchestration
Docker Networking
Docker networking modes, custom networks, DNS resolution, and multi-host connectivity patterns
Dockerfile Optimization
Multi-stage builds, layer caching, and image size optimization for production Docker images
Kubernetes Autoscaling
Kubernetes autoscaling with HPA, VPA, Cluster Autoscaler, and event-driven scaling with KEDA