Skip to main content
Technology & EngineeringContainerization220 lines

Helm Charts

Helm chart creation, templating, dependency management, and release lifecycle for Kubernetes

Quick Summary12 lines
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 lines
Paste into your CLAUDE.md or agent config

Helm 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 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.

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 lint and helm template in CI. Template rendering errors only surface at install time if you do not validate earlier. Running helm lint and piping helm template through kubectl apply --dry-run=client catches syntax errors, missing values, and invalid Kubernetes manifests before they reach a cluster.

  • Using helm install in CI/CD instead of helm upgrade --install. helm install fails if the release already exists, making your pipeline non-idempotent. helm upgrade --install is safe to run repeatedly: it installs on first run and upgrades thereafter.

  • Undocumented values.yaml. A values.yaml file 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 --namespace flag control placement and use {{ .Release.Namespace }} when namespace references are needed within templates.

Common Pitfalls

  • 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.

Install this skill directly: skilldb add containerization-skills

Get CLI access →