**Version**: 1.0.0 | **Target Size**: <100KB | **Purpose**: Fast reference for Helm chart development and deployment
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
REFERENCE.mdVersion: 1.0.0 | Target Size: <100KB | Purpose: Fast reference for Helm chart development and deployment
Helm is a package manager for Kubernetes that simplifies application deployment through charts - pre-configured Kubernetes resource templates. This quick reference provides essential patterns for chart creation, templating, and deployment.
When to Load This Skill:
Chart.yaml, values.yaml, templates/ directory--tools=helm flagProgressive Disclosure:
Standard Helm chart directory layout:
mychart/
├── Chart.yaml # Chart metadata (name, version, description)
├── values.yaml # Default configuration values
├── charts/ # Chart dependencies (subcharts)
├── templates/ # Kubernetes manifest templates
│ ├── NOTES.txt # Post-install usage notes
│ ├── _helpers.tpl # Named templates (partials)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ └── secret.yaml
├── .helmignore # Files to exclude from packaging
└── README.md # Chart documentation
Key Concepts:
helm dependency update)Chart.yaml defines chart metadata and dependencies:
apiVersion: v2 # Helm 3 uses v2
name: webapp # Chart name (DNS-compatible)
version: 1.2.3 # Chart version (SemVer)
appVersion: "2.0.1" # Application version
description: Production web application with autoscaling
type: application # application or library
keywords:
- webapp
- kubernetes
- production
home: https://example.com
sources:
- https://github.com/example/webapp
maintainers:
- name: DevOps Team
email: devops@example.com
dependencies:
- name: postgresql
version: "12.1.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # Optional: conditional dependency
- name: redis
version: "17.3.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
Version Guidelines:
Dependency Types:
postgresql.enabled: true)--set tags.database=false)Helm uses Go templates with additional functions.
# Access values from values.yaml
{{ .Values.replicaCount }}
{{ .Values.image.repository }}
{{ .Values.service.port }}
# Release information
{{ .Release.Name }} # Release name (helm install <name>)
{{ .Release.Namespace }} # Kubernetes namespace
{{ .Release.IsInstall }} # true if installing (not upgrading)
{{ .Release.IsUpgrade }} # true if upgrading
{{ .Release.Revision }} # Revision number
# Chart metadata (from Chart.yaml)
{{ .Chart.Name }}
{{ .Chart.Version }}
{{ .Chart.AppVersion }}
# Kubernetes cluster capabilities
{{ .Capabilities.APIVersions.Has "apps/v1" }}
{{ .Capabilities.KubeVersion.Major }}
{{ .Capabilities.KubeVersion.Minor }}
# Template context
{{ .Template.Name }} # Current template file name
{{ .Template.BasePath }} # Template directory path
# If/Else conditionals
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
# ... ingress spec
{{- end }}
# If/Else with multiple conditions
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
# Create new PVC
{{- else if .Values.persistence.existingClaim }}
# Use existing PVC
{{- else }}
# Use emptyDir
{{- end }}
# Range (loops)
{{- range .Values.extraEnv }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
# Range with key-value pairs
{{- range $key, $value := .Values.config }}
{{ $key }}: {{ $value | quote }}
{{- end }}
# With (scope modification)
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
String Functions:
{{ .Values.name | quote }} # "webapp"
{{ .Values.name | upper }} # WEBAPP
{{ .Values.name | lower }} # webapp
{{ .Values.name | title }} # Webapp
{{ .Values.name | trim }} # Remove whitespace
{{ .Values.name | trunc 10 }} # Truncate to 10 chars
{{ .Values.name | default "default" }} # Default if empty
Type Conversion:
{{ .Values.port | toString }} # Convert to string
{{ .Values.replicas | int }} # Convert to integer
{{ .Values.enabled | ternary "yes" "no" }} # Conditional value
Encoding:
{{ .Values.config | b64enc }} # Base64 encode
{{ .Values.secret | b64dec }} # Base64 decode
{{ .Values.data | toJson }} # Convert to JSON
{{ .Values.data | toYaml }} # Convert to YAML
Lists and Dictionaries:
{{ .Values.list | first }} # First element
{{ .Values.list | rest }} # All but first
{{ .Values.list | last }} # Last element
{{ .Values.list | has "item" }} # Check if contains
{{ .Values.dict | keys }} # Dictionary keys
{{ .Values.dict | values }} # Dictionary values
Kubernetes-Specific:
{{ include "mychart.labels" . | nindent 4 }} # Include named template with indent
{{ .Values.resources | toYaml | nindent 8 }} # Convert resources to YAML
{{ .Release.Name | trunc 63 | trimSuffix "-" }} # DNS-safe name (≤63 chars)
Define in templates/_helpers.tpl:
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a fully qualified app name.
*/}}
{{- define "mychart.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 "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 }}
Use in templates:
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
values.yaml provides default configuration values:
# Replica configuration
replicaCount: 3
# Image configuration
image:
repository: myapp
pullPolicy: IfNotPresent
tag: "" # Defaults to .Chart.AppVersion
# Image pull secrets
imagePullSecrets:
- name: regcred
# Service account
serviceAccount:
create: true
annotations: {}
name: ""
# Pod annotations
podAnnotations: {}
# Pod security context
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
# Container security context
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Service configuration
service:
type: ClusterIP
port: 80
targetPort: 8080
# Ingress configuration
ingress:
enabled: false
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: example-tls
hosts:
- example.com
# Resources
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
# Autoscaling
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# Node selector
nodeSelector: {}
# Tolerations
tolerations: []
# Affinity
affinity: {}
# Persistence
persistence:
enabled: true
storageClass: "gp3"
size: 10Gi
accessMode: ReadWriteOnce
# Environment variables
env:
- name: APP_ENV
value: production
- name: LOG_LEVEL
value: info
# ConfigMap data
config:
database_host: postgres.default.svc.cluster.local
database_port: "5432"
cache_ttl: "3600"
# Secrets (base64 encoded)
secrets:
database_password: "" # Override at install time
Environment-Specific Overrides:
# Development values (values-dev.yaml)
replicaCount: 1
resources:
limits:
cpu: 200m
memory: 256Mi
# Production values (values-prod.yaml)
replicaCount: 5
resources:
limits:
cpu: 1000m
memory: 1Gi
autoscaling:
enabled: true
maxReplicas: 20
# Create new chart
helm create mychart
# Validate chart structure
helm lint mychart/
# Package chart into archive
helm package mychart/
# Output: mychart-1.0.0.tgz
# Render templates locally (dry-run)
helm template myrelease mychart/
helm template myrelease mychart/ --values values-prod.yaml
# Show computed values
helm show values mychart/
# Install chart
helm install myrelease mychart/
helm install myrelease mychart/ --namespace production --create-namespace
# Install with custom values
helm install myrelease mychart/ --values values-prod.yaml
helm install myrelease mychart/ --set replicaCount=5 --set image.tag=v2.0.0
# Dry-run installation (validate without installing)
helm install myrelease mychart/ --dry-run --debug
# Upgrade release
helm upgrade myrelease mychart/
helm upgrade myrelease mychart/ --values values-prod.yaml --set image.tag=v2.1.0
# Upgrade or install (install if doesn't exist)
helm upgrade --install myrelease mychart/
# Rollback to previous revision
helm rollback myrelease
helm rollback myrelease 3 # Rollback to specific revision
# Uninstall release
helm uninstall myrelease
helm uninstall myrelease --namespace production
# Keep history after uninstall
helm uninstall myrelease --keep-history
# List releases
helm list
helm list --namespace production
helm list --all-namespaces
# Show release status
helm status myrelease
# Show release history
helm history myrelease
# Get release values
helm get values myrelease
helm get values myrelease --revision 3
# Get release manifest
helm get manifest myrelease
# Get all release information
helm get all myrelease
# Add repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
# Update repositories
helm repo update
# Search repositories
helm search repo postgresql
helm search repo bitnami/postgresql --versions
# Show chart information
helm show chart bitnami/postgresql
helm show values bitnami/postgresql
helm show readme bitnami/postgresql
# Update chart dependencies
helm dependency update mychart/
# List dependencies
helm dependency list mychart/
# Build dependencies (download to charts/ directory)
helm dependency build mychart/
dependencies:
# Database dependency
- name: postgresql
version: "12.1.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
tags:
- database
# Cache dependency
- name: redis
version: "17.3.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
tags:
- cache
# Local chart dependency
- name: common
version: "1.0.0"
repository: file://../common
Parent chart values.yaml:
# Configure postgresql subchart
postgresql:
enabled: true
auth:
username: myapp
password: changeme
database: myapp_production
primary:
persistence:
enabled: true
size: 20Gi
# Configure redis subchart
redis:
enabled: true
auth:
enabled: false
master:
persistence:
enabled: false
# Enable/disable dependencies
helm install myrelease mychart/ \
--set postgresql.enabled=true \
--set redis.enabled=false
# Using tags
helm install myrelease mychart/ \
--set tags.database=true \
--set tags.cache=false
Chart.yaml:
dependencies:
- name: postgresql
version: "12.1.0"
repository: https://charts.bitnami.com/bitnami
import-values:
- child: auth.password
parent: dbPassword
Access in templates:
env:
- name: DB_PASSWORD
value: {{ .Values.dbPassword }}
# 1. Dry-run to validate
helm install myrelease mychart/ --dry-run --debug
# 2. Install to staging
helm install myrelease mychart/ \
--namespace staging \
--create-namespace \
--values values-staging.yaml
# 3. Verify installation
helm status myrelease --namespace staging
kubectl get pods --namespace staging
# 4. Promote to production
helm install myrelease mychart/ \
--namespace production \
--create-namespace \
--values values-prod.yaml
# 1. Check current values
helm get values myrelease --namespace production
# 2. Dry-run upgrade
helm upgrade myrelease mychart/ \
--namespace production \
--values values-prod.yaml \
--set image.tag=v2.1.0 \
--dry-run --debug
# 3. Perform upgrade
helm upgrade myrelease mychart/ \
--namespace production \
--values values-prod.yaml \
--set image.tag=v2.1.0
# 4. Monitor rollout
kubectl rollout status deployment/myrelease-webapp --namespace production
# 5. Verify upgrade
helm status myrelease --namespace production
# 1. Check release history
helm history myrelease --namespace production
# 2. Rollback to previous version
helm rollback myrelease --namespace production
# 3. Rollback to specific revision
helm rollback myrelease 3 --namespace production
# 4. Verify rollback
helm status myrelease --namespace production
kubectl rollout status deployment/myrelease-webapp --namespace production
# Uninstall release (removes all resources)
helm uninstall myrelease --namespace production
# Keep history (allows rollback after uninstall)
helm uninstall myrelease --namespace production --keep-history
# Delete namespace
kubectl delete namespace production
# Lint chart for issues
helm lint mychart/
# Lint with custom values
helm lint mychart/ --values values-prod.yaml
# Strict linting (fail on warnings)
helm lint mychart/ --strict
Common lint issues:
{{ required }} for critical values# Render templates locally
helm template myrelease mychart/
# Render with custom values
helm template myrelease mychart/ --values values-prod.yaml
# Debug template rendering
helm template myrelease mychart/ --debug
# Validate against Kubernetes API
helm template myrelease mychart/ | kubectl apply --dry-run=client -f -
Create test pod (templates/tests/test-connection.yaml):
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "mychart.fullname" . }}-test-connection"
labels:
{{- include "mychart.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test # Mark as test hook
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
Run tests:
# Run all tests for release
helm test myrelease --namespace production
# Show test logs
helm test myrelease --namespace production --logs
# Cleanup test pods after run
kubectl delete pod -l 'helm.sh/hook=test' --namespace production
# Pin specific image versions (avoid :latest)
image:
repository: myapp
tag: "v2.1.0" # Explicit version
pullPolicy: IfNotPresent
# Use SHA256 digests for immutability
image:
repository: myapp
digest: "sha256:abc123..." # Immutable image
# Create minimal service account
serviceAccount:
create: true
annotations:
# AWS IRSA example
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
name: ""
# Define RBAC role (templates/role.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "mychart.fullname" . }}
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
# Pod security context
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
# Container security context
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
# Use Kubernetes Secrets (not ConfigMaps)
apiVersion: v1
kind: Secret
metadata:
name: {{ include "mychart.fullname" . }}
type: Opaque
data:
# Base64 encoded values
database-password: {{ .Values.secrets.databasePassword | b64enc | quote }}
# Reference secrets in pods
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "mychart.fullname" . }}
key: database-password
External Secrets Integration:
# Use External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: {{ include "mychart.fullname" . }}
spec:
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: {{ include "mychart.fullname" . }}-secrets
data:
- secretKey: database-password
remoteRef:
key: /prod/myapp/db-password
# Restrict pod network access
{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "mychart.fullname" . }}
spec:
podSelector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: {{ .Values.service.targetPort }}
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
{{- end }}
# Always set resource limits
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
# Force required values at install time
{{- if not .Values.secrets.databasePassword }}
{{- fail "secrets.databasePassword is required" }}
{{- end }}
# Or use required function
database:
password: {{ required "Database password required" .Values.secrets.databasePassword }}
Chart.yaml:
apiVersion: v2
name: webapp
version: 1.0.0
appVersion: "1.0"
description: Simple web application
values.yaml:
replicaCount: 2
image:
repository: nginx
tag: "1.21"
service:
port: 80
templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: webapp
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 80
templates/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
spec:
type: ClusterIP
ports:
- port: {{ .Values.service.port }}
targetPort: 80
selector:
app: {{ .Release.Name }}
Install:
helm install myapp webapp/
For Advanced Patterns:
Common Use Cases:
Progressive Disclosure: Start here for quick reference, load REFERENCE.md for comprehensive patterns and production examples.
Performance Target: <100ms skill loading (this file ~75KB)
Last Updated: 2025-10-23 | Version: 1.0.0