Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design, organize, and manage Helm charts for templating and packaging Kubernetes applications with reusable configurations. Use when creating Helm charts, packaging Kubernetes applications, or implementing templated deployments.
.claude/skills/microck-helm-chart-scaffolding/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | 223% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 196% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 158% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 205% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 98% | 0% |
Comprehensive guidance for creating, organizing, and managing Helm charts for packaging and deploying Kubernetes applications.
This skill provides step-by-step instructions for building production-ready Helm charts, including chart structure, templating patterns, values management, and validation strategies.
Use this skill when you need to:
Helm is the package manager for Kubernetes that:
Create new chart:
bashhelm 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 ignoreChart metadata defines the package:
yamlapiVersion: 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
Organize values hierarchically:
yaml# 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
Use Go templating with Helm functions:
templates/deployment.yaml:
yamlapiVersion: 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 }}
templates/_helpers.tpl:
yaml{{/* 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 }}
Add dependencies in Chart.yaml:
yamldependencies: - name: postgresql version: "12.0.0" repository: "https://charts.bitnami.com/bitnami" condition: postgresql.enabled
Update dependencies:
bashhelm dependency update helm dependency build
Override dependency values:
yaml# values.yaml postgresql: enabled: true auth: database: myapp username: myapp password: changeme primary: persistence: enabled: true size: 10Gi
Validation commands:
bash# 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:
bash#!/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
Package the chart:
bashhelm package my-app/ # Creates: my-app-1.0.0.tgz
Create chart repository:
bash# 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:
bashhelm repo add my-repo https://charts.example.com helm repo update helm install my-app my-repo/my-app
Environment-specific values files:
my-app/
├── values.yaml # Defaults
├── values-dev.yaml # Development
├── values-staging.yaml # Staging
└── values-prod.yaml # Productionvalues-prod.yaml:
yamlreplicaCount: 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:
bashhelm install my-app ./my-app -f values-prod.yaml --namespace production
Pre-install hook:
yaml# 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:
yaml# 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:
bashhelm test my-app
yaml{{- if .Values.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "my-app.fullname" . }} spec: # ... {{- end }}
yamlenv: {{- range .Values.env }} - name: {{ .name }} value: {{ .value | quote }} {{- end }}
yamldata: config.yaml: | {{- .Files.Get "config/application.yaml" | nindent 4 }}
yamlglobal: imageRegistry: docker.io imagePullSecrets: - name: regcred # Use in templates: image: {{ .Values.global.imageRegistry }}/{{ .Values.image.repository }}
Template rendering errors:
bashhelm template my-app ./my-app --debug
Dependency issues:
bashhelm dependency update helm dependency list
Installation failures:
bashhelm install my-app ./my-app --dry-run --debug kubectl get events --sort-by='.lastTimestamp'
assets/Chart.yaml.template - Chart metadata templateassets/values.yaml.template - Values structure templatescripts/validate-chart.sh - Validation scriptreferences/chart-structure.md - Detailed chart organizationk8s-manifest-generator - For creating base Kubernetes manifestsgitops-workflow - For automated Helm chart deployments| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 17,641 | 16,678 | -5% | 1 | 1 | 0% | 3,348 | 6,695 | +100% | 0 | 0 | — |
case-02 | pass→pass | 8,513 | 5,742 | -33% | 1 | 1 | 0% | 1,447 | 4,286 | +196% | 0 | 0 | — |
case-03 | pass→pass | 10,082 | 7,803 | -23% | 1 | 1 | 0% | 1,851 | 4,778 | +158% | 0 | 0 | — |
case-04 | pass→pass | 7,347 | 3,844 | -48% | 1 | 1 | 0% | 1,274 | 3,891 | +205% | 0 | 0 | — |
case-05 | pass→pass | 13,349 | 9,454 | -29% | 1 | 1 | 0% | 2,620 | 5,176 | +98% | 0 | 0 | — |
case-06 | pass→pass | 7,281 | 6,320 | -13% | 1 | 1 | 0% | 1,316 | 4,450 | +238% | 0 | 0 | — |
case-07 | pass→pass | 6,534 | 5,145 | -21% | 1 | 1 | 0% | 1,194 | 4,250 | +256% | 0 | 0 | — |
case-08 | pass→pass | 14,595 | 9,696 | -34% | 1 | 1 | 0% | 2,816 | 5,185 | +84% | 0 | 0 | — |
case-09 | pass→pass | 6,940 | 5,099 | -27% | 1 | 1 | 0% | 1,172 | 4,148 | +254% | 0 | 0 | — |
case-10 | pass→pass | 3,465 | 2,949 | -15% | 1 | 1 | 0% | 595 | 3,784 | +536% | 0 | 0 | — |
case-11 | fail→pass | 7,215 | 3,389 | -53% | 1 | 1 | 0% | 1,187 | 3,837 | +223% | 0 | 0 | — |
case-12 | pass→pass | 3,899 | 3,474 | -11% | 1 | 1 | 0% | 620 | 3,831 | +518% | 0 | 0 | — |
case-13 | pass→pass | 6,054 | 4,660 | -23% | 1 | 1 | 0% | 1,158 | 4,010 | +246% | 0 | 0 | — |
case-14 | pass→pass | 7,396 | 5,714 | -23% | 1 | 1 | 0% | 1,274 | 4,281 | +236% | 0 | 0 | — |
case-15 | pass→pass | 8,021 | 4,372 | -45% | 1 | 1 | 0% | 1,439 | 4,040 | +181% | 0 | 0 | — |
case-16 | pass→pass | 8,179 | 7,870 | -4% | 1 | 1 | 0% | 1,485 | 4,809 | +224% | 0 | 0 | — |
case-17 | pass→pass | 4,416 | 5,253 | +19% | 1 | 1 | 0% | 699 | 4,193 | +500% | 0 | 0 | — |
case-18 | pass→pass | 5,392 | 3,271 | -39% | 1 | 1 | 0% | 947 | 3,863 | +308% | 0 | 0 | — |
case-19 | pass→pass | 3,058 | 3,474 | +14% | 1 | 1 | 0% | 486 | 3,815 | +685% | 0 | 0 | — |
case-20 | pass→pass | 13,624 | 11,746 | -14% | 1 | 1 | 0% | 2,477 | 5,569 | +125% | 0 | 0 | — |
case-21 | pass→pass | 14,093 | 11,633 | -17% | 1 | 1 | 0% | 2,440 | 5,454 | +124% | 0 | 0 | — |
case-22 | pass→pass | 11,524 | 12,427 | +8% | 1 | 1 | 0% | 2,443 | 5,753 | +135% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.