Step-by-step guide for authoring a production-grade Helm chart from scratch — directory layout, values design, template hygiene, helpers, chart tests, and lint/security gates — so workloads are packaged reproducibly and safely promoted across environments.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cloud-native-kubernetes:helm-chart-authoringThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use when packaging a Kubernetes workload as a Helm chart for the first time, or when an existing chart needs a structural overhaul (template sprawl, missing defaults, no tests).
Use when packaging a Kubernetes workload as a Helm chart for the first time, or when an existing chart needs a structural overhaul (template sprawl, missing defaults, no tests).
charts/my-service/
├── Chart.yaml # name, version (semver), appVersion
├── values.yaml # all defaults — must be complete and runnable
├── values-prod.yaml # prod overrides only (NOT defaults)
├── templates/
│ ├── _helpers.tpl # named templates: labels, selector, fullname
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ ├── serviceaccount.yaml
│ ├── networkpolicy.yaml
│ └── NOTES.txt
└── tests/
└── connection-test.yaml # helm test job
Chart.yaml minimum:
apiVersion: v2
name: my-service
version: 1.0.0 # chart version (semver)
appVersion: "0.1.0" # application version (informational)
values.yaml must produce a valid, runnable chart on helm install with no extra flags.image.repository / image.tag over imageRepository / imageTag.resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
{{- define "my-service.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "my-service.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 "my-service.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
Use include (not template) in templates so output can be piped through indent.
| Item | Pattern |
|---|---|
Image tag is never latest | `image: "{{ .Values.image.repository }}:{{ .Values.image.tag |
imagePullPolicy defaults to IfNotPresent | Override to Always only for mutable tags |
securityContext set on pod and container | runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false |
serviceAccountName explicit | Even if default, be explicit |
| Probes templated from values | Never hardcoded paths |
| PodDisruptionBudget included | minAvailable: 1 default |
| HPA min/max from values | Never hardcoded |
# tests/connection-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "my-service.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
restartPolicy: Never
containers:
- name: wget
image: busybox
command: ["wget", "--timeout=5", "-O", "/dev/null",
"http://{{ include "my-service.fullname" . }}:{{ .Values.service.port }}/healthz"]
Run with helm test <release> after install/upgrade in CI.
helm lint charts/my-service/ --values charts/my-service/values-prod.yaml
# Strict schema validation
helm template charts/my-service/ | kubeval --strict
# Security misconfig scan
helm template charts/my-service/ | kubesec scan -
# OPA/Conftest policy gates
helm template charts/my-service/ | conftest test -
Add these as CI steps before pushing to a chart registry (OCI or Chartmuseum).
latest image tag in values.yaml — unpinned images break reproducibility and rollback._helpers.tpl — copy-pasted label blocks across templates drift and cause selector mismatches on upgrade.values.yaml missing resource defaults — a chart installable with 0 CPU request schedules a pod the scheduler can't reason about.required "" on every value — forces callers to spell out things that have sane defaults; reserve required for genuinely unconfigurable values (e.g., a cluster-specific hostname).helm install succeeding means the Kubernetes API accepted the manifests, not that the app is running.npx claudepluginhub mcorbett51090/ravenclaude --plugin cloud-native-kubernetesGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.