Helm Chart Scaffolding Comprehensive guidance for creating, organizing, and managing Helm charts for packaging and deploying Kubernetes applications. Purpose This skill provides step-by-step instructions for building production-ready Helm charts, including chart structure, templating patterns, values management, and validation strategies. When to Use This Skill Use this skill when you need to: Create new Helm charts from scratch Package Kubernetes applications for distribution Manage multi-environment deployments with Helm Implement templating for reusable Kubernetes manifests Set up Helm chart repositories Follow Helm best practices and conventions Helm Overview Helm is the package manager for Kubernetes that: Templates Kubernetes manifests for reusability Manages application releases and rollbacks Handles dependencies between charts Provides version control for deployments Simplifies configuration management across environments Step-by-Step Workflow 1. Initialize Chart Structure Create new chart: helm create my-app Standard chart structure: my-app/ ├── Chart.yaml # Chart metadata ├── values.yaml # Default configuration values ├── charts/ # Chart dependencies ├── templates/ # Kubernetes manifest templates │ ├── NOTES.txt # Post-install notes │ ├── _helpers.tpl # Template helpers │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── serviceaccount.yaml │ ├── hpa.yaml │ └── tests/ │ └── test-connection.yaml └── .helmignore # Files to ignore 2. Configure Chart.yaml Chart metadata defines the package: apiVersion : v2 name : my - app description : A Helm chart for My Application type : application version : 1.0.0
Chart version
appVersion : "2.1.0"
Application version
Keywords for chart discovery
keywords : - web - api - backend
Maintainer information
maintainers : - name : DevOps Team email : devops@example.com url : https : //github.com/example/my - app
Source code repository
sources : - https : //github.com/example/my - app
Homepage
home : https : //example.com
Chart icon
icon : https : //example.com/icon.png
Dependencies
dependencies : - name : postgresql version : "12.0.0" repository : "https://charts.bitnami.com/bitnami" condition : postgresql.enabled - name : redis version : "17.0.0" repository : "https://charts.bitnami.com/bitnami" condition : redis.enabled Reference: See assets/Chart.yaml.template for complete example 3. Design values.yaml Structure Organize values hierarchically:
Image configuration
image : repository : myapp tag : "1.0.0" pullPolicy : IfNotPresent
Number of replicas
replicaCount : 3
Service configuration
service : type : ClusterIP port : 80 targetPort : 8080
Ingress configuration
ingress : enabled : false className : nginx hosts : - host : app.example.com paths : - path : / pathType : Prefix
Resources
resources : requests : memory : "256Mi" cpu : "250m" limits : memory : "512Mi" cpu : "500m"
Autoscaling
autoscaling : enabled : false minReplicas : 2 maxReplicas : 10 targetCPUUtilizationPercentage : 80
Environment variables
env : - name : LOG_LEVEL value : "info"
ConfigMap data
configMap : data : APP_MODE : production
Dependencies
postgresql : enabled : true auth : database : myapp username : myapp redis : enabled : false Reference: See assets/values.yaml.template for complete structure 4. Create Template Files Use Go templating with Helm functions: templates/deployment.yaml: apiVersion : apps/v1 kind : Deployment metadata : name : { { include "my - app.fullname" . } } labels : { { - include "my - app.labels" . | nindent 4 } } spec : { { - if not .Values.autoscaling.enabled } } replicas : { { .Values.replicaCount } } { { - end } } selector : matchLabels : { { - include "my - app.selectorLabels" . | nindent 6 } } template : metadata : labels : { { - include "my - app.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 : { { .Values.service.targetPort } } resources : { { - toYaml .Values.resources | nindent 12 } } env : { { - toYaml .Values.env | nindent 12 } } 5. Create Template Helpers templates/_helpers.tpl: { { / Expand the name of the chart. / } } { { - define "my - app.name" - } } { { - default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix " - " } } { { - end } } { { / Create a default fully qualified app name. / } } { { - define "my - app.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 "my - app.labels" - } } helm.sh/chart : { { include "my - app.chart" . } } { { include "my - app.selectorLabels" . } } { { - if .Chart.AppVersion } } app.kubernetes.io/version : { { .Chart.AppVersion | quote } } { { - end } } app.kubernetes.io/managed-by : { { .Release.Service } } { { - end } } { { / Selector labels / } } { { - define "my - app.selectorLabels" - } } app.kubernetes.io/name : { { include "my - app.name" . } } app.kubernetes.io/instance : { { .Release.Name } } { { - end } } 6. Manage Dependencies Add dependencies in Chart.yaml: dependencies : - name : postgresql version : "12.0.0" repository : "https://charts.bitnami.com/bitnami" condition : postgresql.enabled Update dependencies: helm dependency update helm dependency build Override dependency values:
values.yaml
postgresql : enabled : true auth : database : myapp username : myapp password : changeme primary : persistence : enabled : true size : 10Gi 7. Test and Validate Validation commands:
Lint the chart
helm lint my-app/
Dry-run installation
helm install my-app ./my-app --dry-run --debug
Template rendering
helm template my-app ./my-app
Template with values
helm template my-app ./my-app -f values-prod.yaml
Show computed values
helm show values ./my-app Validation script:
!/bin/bash
set -e echo "Linting chart..." helm lint . echo "Testing template rendering..." helm template test-release . --dry-run echo "Checking for required values..." helm template test-release . --validate echo "All validations passed!" Reference: See scripts/validate-chart.sh 8. Package and Distribute Package the chart: helm package my-app/
Creates: my-app-1.0.0.tgz
Create chart repository:
Create index
helm repo index .
Upload to repository
AWS S3 example
aws s3 sync . s3://my-helm-charts/ --exclude "" --include ".tgz" --include "index.yaml" Use the chart: helm repo add my-repo https://charts.example.com helm repo update helm install my-app my-repo/my-app 9. Multi-Environment Configuration Environment-specific values files: my-app/ ├── values.yaml # Defaults ├── values-dev.yaml # Development ├── values-staging.yaml # Staging └── values-prod.yaml # Production values-prod.yaml: replicaCount : 5 image : tag : "2.1.0" resources : requests : memory : "512Mi" cpu : "500m" limits : memory : "1Gi" cpu : "1000m" autoscaling : enabled : true minReplicas : 3 maxReplicas : 20 ingress : enabled : true hosts : - host : app.example.com paths : - path : / pathType : Prefix postgresql : enabled : true primary : persistence : size : 100Gi Install with environment: helm install my-app ./my-app -f values-prod.yaml --namespace production 10. Implement Hooks and Tests Pre-install hook:
templates/pre-install-job.yaml
apiVersion : batch/v1 kind : Job metadata : name : { { include "my - app.fullname" . } } - db - setup annotations : "helm.sh/hook" : pre - install "helm.sh/hook-weight" : "-5" "helm.sh/hook-delete-policy" : hook - succeeded spec : template : spec : containers : - name : db - setup image : postgres : 15 command : [ "psql" , "-c" , "CREATE DATABASE myapp" ] restartPolicy : Never Test connection:
templates/tests/test-connection.yaml
apiVersion : v1 kind : Pod metadata : name : " { { include "my - app.fullname" . } } - test - connection" annotations : "helm.sh/hook" : test spec : containers : - name : wget image : busybox command : [ 'wget' ] args : [ '{{ include "my-app.fullname" . }}:{{ .Values.service.port }}' ] restartPolicy : Never Run tests: helm test my-app Common Patterns Pattern 1: Conditional Resources { { - if .Values.ingress.enabled } } apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : { { include "my - app.fullname" . } } spec :
...
{ { - end } } Pattern 2: Iterating Over Lists env : { { - range .Values.env } } - name : { { .name } } value : { { .value | quote } } { { - end } } Pattern 3: Including Files data : config.yaml : | {{- .Files.Get "config/application.yaml" | nindent 4 }} Pattern 4: Global Values global : imageRegistry : docker.io imagePullSecrets : - name : regcred
Use in templates:
image : { { .Values.global.imageRegistry } } / { { .Values.image.repository } } Best Practices Use semantic versioning for chart and app versions Document all values in values.yaml with comments Use template helpers for repeated logic Validate charts before packaging Pin dependency versions explicitly Use conditions for optional resources Follow naming conventions (lowercase, hyphens) Include NOTES.txt with usage instructions Add labels consistently using helpers Test installations in all environments Troubleshooting Template rendering errors: helm template my-app ./my-app --debug Dependency issues: helm dependency update helm dependency list Installation failures: helm install my-app ./my-app --dry-run --debug kubectl get events --sort-by = '.lastTimestamp'