Helm chart maintenance
Organize Chart.yaml, values.schema.json, overridable templates, and hook lifecycles; keep appVersion and Chart semver aligned; gate PRs with lint, template, and (optional) kubeconform.
The SKILL requires sensible values.yaml defaults and a README listing key values; avoid hard-coded image tags—inject via values and a global section.
Declare compatible ranges for subcharts/library charts; call out CRD and hook ordering risks on upgrades; route secrets through External Secrets / SealedSecrets references—never commit plaintext in the chart.
Chart release pipeline (local → CI → cluster)
[ Chart dir & version bump ]
│
▼
┌─────────────┐ Chart.yaml / values / schema aligned; README updated
│ Local checks │──── helm lint, helm template (+ optional kubeconform)
└─────────────┘
│
▼
┌─────────────┐ ct lint, kind install matrix, artifact push
│ CI / CD │──── semver; major + migration notes on breaking changes
└─────────────┘
│
▼
┌─────────────┐ install / upgrade; history & rollback notes match release skill
│ Cluster rel │──── hook & CRD ordering documented in runbooks
└─────────────┘
Before merge: rendered templates should diff cleanly; prod overlays (e.g. values-prod.yaml) are deltas only—no secrets in git.
Helm Chart directory structure explained:
mychart/
├── Chart.yaml # Chart metadata (name, version, appVersion)
├── values.yaml # Default values (usable out of the box, document all exported keys)
├── values.schema.json # Type constraints and required field validation
├── README.md # Key values explained with usage examples
├── charts/ # Subchart dependencies (populated by helm dependency update)
├── templates/
│ ├── _helpers.tpl # Named templates (fullname, standard labels)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ ├── serviceaccount.yaml
│ ├── NOTES.txt # Usage hints printed after install/upgrade
│ └── tests/
│ └── test-connection.yaml # helm test Pod
├── ci/ # CI values files used by chart-testing
│ └── ci-values.yaml
└── .helmignore # Exclude files from Chart package
Values & schema
Use values.schema.json for types and required keys; defaults should suit a minimal demo. Share cross-subchart config under global and document override precedence (-f order, --set).
- Images: split
repository+tag; tags usually come from env-specific values, not literals in templates. - Resources/replicas: separate prod/stage values files; document switches that must stay off in prod (e.g. debug).
- Docs: one line per exported value; on breaking renames keep an alias or migration note for one release.
values.yaml design principles (required/optional/env-specific) with example:
# values.yaml — defaults usable out of the box (minimal demo environment)
# Values that must be overridden externally default to "" and are marked required in schema
image:
repository: myregistry/myapp # required: injected by CI
tag: "" # required: overridden by env values (never hardcode latest)
pullPolicy: IfNotPresent
replicaCount: 1 # dev default 1; prod values override to 3+
service:
type: ClusterIP
port: 80
ingress:
enabled: false # disabled by default; prod values enable
className: nginx
host: "" # must be provided by env values
tls: false
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: false # recommended to enable in production
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
# debug: false # must NOT be enabled in production! see values-prod.yaml comment
---
# values-prod.yaml — deltas only
# image.tag is injected by CI pipeline with --set image.tag=1.2.3
replicaCount: 3
ingress:
enabled: true
host: myapp.example.com
tls: true
autoscaling:
enabled: true
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
Templates & parameterization
Use {{ .Values }}, required, default, and tpl for DRY templates; wire labels/selectors to the same variables to avoid drift. Factor helpers in _helpers.tpl for full names and standard labels.
Prefer
includefor named templates; comment in-chart conventions- Configurable probes, resources,
securityContext - Render only needed resources; document conditionals/API version checks
Avoid
- Hard-coded environment names or full image URIs without tag variables
- Ignoring
nindent/YAML spacing that creates invalid manifests - Depending on undocumented Helm internals
_helpers.tpl helper functions (fullname, standard labels, selectorLabels):
{{/*
_helpers.tpl — named templates for mychart
mychart.fullname: Chart name + release name, truncated to 63 chars
*/}}
{{- define "mychart.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 }}
{{/*
mychart.labels: standard label set for all resources
*/}}
{{- define "mychart.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
mychart.selectorLabels: Deployment selector (no version to avoid update conflicts)
*/}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/* Usage in deployment.yaml */}}
{{/*
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "mychart.selectorLabels" . | nindent 8 }}
*/}}
Dependencies, hooks & secrets
Chart.yaml dependencies should use compatible ranges; note subchart upgrades in CHANGELOG. Hooks need hook-weight and delete policies; if CRD ordering conflicts, dedicate a section in the SKILL or runbook.
- Testing: chart-testing (ct) plus kind install smoke matrix.
- Rollback: align
helm rollbackand release history notes with your release skill. - Multi-env: rules for
values-prod.yamloverlays and forbidden items (e.g. dev endpoints).
CI checks & environments
PRs run helm lint and helm template; with kubeconform, pin Kubernetes versions and CRD sources. Store rendered manifests as artifacts for reviewable diffs.
Stay consistent with raw manifest skills: rendered output should meet the same label, probe, and security baseline.
Helm command snippet lab
Fill chart path and release name, select value files and flags, and copy ready-to-run helm lint / helm template snippets (tune flags for your Helm version).
helm template prints to stdout; pipe through | tee /tmp/rendered.yaml before kubeconform. Maintain install/upgrade commands separately with cluster creds and team policies like --atomic.
---
name: helm-chart-maintenance
description: Maintain Helm charts that lint and template cleanly
tags: [helm, kubernetes, devops, chart]
---
# Directory structure
1. templates/_helpers.tpl defines fullname, labels, selectorLabels named templates
2. values.schema.json constrains types and required fields (auto-validated at helm install)
3. ci/ directory holds minimal CI values files for chart-testing
# Values design
4. Defaults usable out of the box: minimal demo doesn't need extra -f files
5. image.tag injected by env values (--set image.tag=) never hardcoded
6. Env delta files: values-dev.yaml / values-staging.yaml / values-prod.yaml contain deltas only
7. Secrets must NOT appear in values-prod.yaml; use External Secrets / SealedSecrets
# Template best practices
8. All resource labels reference include "mychart.labels" to stay DRY
9. selector and template.labels reference the same selectorLabels, no hand-written strings
10. Conditional rendering: {{ if .Values.ingress.enabled }} generates Ingress only when needed
11. required "message" .Values.image.repository errors immediately on missing required values
# Testing & CI
12. helm lint + helm template | kubeconform required on every PR
13. helm unittest (helm plugin install https://github.com/helm-unittest/helm-unittest) unit-tests template rendering
14. chart-testing (ct) + kind cluster for install/upgrade smoke tests
# Upgrades & rollback
15. Chart.yaml version follows semver; breaking changes bump major with migration notes
16. helm upgrade --atomic rolls back automatically on failure; use --timeout to prevent hangs
17. Document hook weight and delete policies; CRD install ordering gets its own runbook section