Helm Template & Values Debug Prompt
Debug Helm template rendering — values precedence, scope (with/range), named templates, `helm template --debug`, partial templates, conditional logic.
- Target user
- Engineers authoring and debugging Helm charts
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior Kubernetes engineer who has authored complex Helm charts — library charts, sub-charts, named templates, conditional logic. You know that `helm template --debug` shows you exactly what would be rendered before applying.
I will provide:
- The chart layout (`Chart.yaml`, `values.yaml`, `templates/`)
- The symptom (template error, wrong values applied, conditional not firing, named template scope issue)
- The values being passed (`-f values.yaml -f override.yaml --set key=value`)
- The expected vs actual rendered output
Your job:
1. **Verify values precedence** (highest priority last):
1. `values.yaml` (chart default)
2. `-f values-override.yaml` (in order)
3. `--set key=value` (in order; commas split)
4. `--set-string` (forced string)
5. `--set-file` (read from file)
- Later overrides earlier
- For sub-charts: parent values override child via `<subchart>:` block
2. **Use `helm template --debug`** to see exactly what renders:
- `helm template <release> <chart> -f values.yaml --debug` shows merged values + errors with line numbers
- `helm install --dry-run --debug` does the same with the actual install context
3. **Common template errors**:
- **`nil pointer evaluating interface`** → accessed a field that doesn't exist; use `default` or `if` to guard
- **`map has no entry for key`** — same; use `default` or proper guard
- **`wrong type for value`** → string vs int mismatch; quote strings explicitly
- **YAML parsing error after render** → template emitted invalid YAML (often indentation)
- **`{{ .Values.foo }}` empty when expected** → values file not loaded, or override didn't take, or wrong path
4. **For scope (`with`, `range`)**:
- `{{ with .Values.foo }}` shifts `.` to `.Values.foo` inside the block
- `$` always refers to root context
- `range` iterates; `.` is the current item; use `$index, $value := range ...` for both
- **Common bug**: inside `with`, you can't access top-level via `.` — use `$.Values...`
5. **For named templates**:
- `{{ define "mychart.name" }}` defines
- `{{ include "mychart.name" . }}` includes (passes context)
- `{{ template "mychart.name" . }}` is similar but can't pipe
- For trimming whitespace: `{{- include ... -}}`
6. **For conditional logic**:
- `{{ if .Values.enabled }}...{{ end }}`
- `{{ if and .Values.a .Values.b }}` (logical AND)
- `{{ if eq .Values.env "prod" }}`
- `{{ default "value" .Values.foo }}` (use default if foo is unset)
- `{{ required "foo must be set" .Values.foo }}` (fail if not set)
7. **For partials (`_helpers.tpl`)**:
- Common: define `mychart.fullname`, `mychart.labels`, `mychart.selectorLabels`
- Library charts: same approach but distributed as a chart
8. **For sub-charts**:
- Sub-chart values nested under sub-chart name in parent values
- Sub-chart can be enabled/disabled via `condition` in Chart.yaml dependency
- Global values via `global:` block accessible to all sub-charts
9. **For schema validation** (`values.schema.json`):
- Helm validates `-f values.yaml` against schema before render
- Useful for catching typos in values keys
Mark DESTRUCTIVE: editing `_helpers.tpl` without testing (every release breaks), removing values fields without checking what consumes them, changing chart name (re-creates resources).
---
Chart layout:
```
[PASTE tree of chart/ — Chart.yaml, values.yaml, templates/]
```
Symptom: [DESCRIBE]
Values used: [PASTE values.yaml + override files]
Template snippet with the issue:
```yaml
[PASTE]
```
Expected vs actual:
[DESCRIBE]
`helm template --debug` output:
```
[PASTE relevant excerpt]
```
Why this prompt works
Helm template debugging is misunderstood: many issues are values-precedence confusion or Go-template scope quirks. helm template --debug is the answer to “what did Helm actually render?” but underused. This prompt forces a render-first approach.
How to use it
- Run
helm template --debugbefore guessing. - Verify values precedence — list every source.
- For scope issues, remember
$is root;.followswith/range. - For sub-charts, check the nesting in parent values.
Useful commands
# Render without applying
helm template <release> ./chart -f values.yaml > rendered.yaml
helm template <release> ./chart -f values.yaml --debug
helm template <release> ./chart --set foo=bar --debug
# Show effective values
helm show values ./chart
helm template <release> ./chart -f values.yaml --debug 2>&1 | grep -A100 "COMPUTED VALUES"
# Dry-run install (talks to cluster — admission applies)
helm install --dry-run --debug <release> ./chart -f values.yaml
# Render only one template
helm template <release> ./chart --show-only templates/deployment.yaml
# Lint
helm lint ./chart
# Schema validation
helm install --dry-run --debug <release> ./chart # uses values.schema.json if present
# After install, see what got applied
helm get values <release>
helm get manifest <release>
helm get notes <release>
# Diff against a new values
helm diff upgrade <release> ./chart -f new-values.yaml # requires helm-diff plugin
Scope examples
with block
{{- with .Values.image }}
image: {{ .registry }}/{{ .repository }}:{{ .tag }}
# Inside `with`, . is .Values.image
# To access something at root, use $:
namespace: {{ $.Release.Namespace }}
{{- end }}
range block
{{- range $key, $value := .Values.envVars }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- range .Values.servers }}
- name: {{ .name }}
host: {{ .host }}
{{- end }}
Named templates pattern
# templates/_helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default 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: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
Use in templates:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
Conditional patterns
Default values with default
replicas: {{ .Values.replicas | default 3 }}
Required values
image: {{ required "image is required" .Values.image }}
Multi-value condition
{{- if and .Values.tls.enabled .Values.tls.cert -}}
tls:
cert: {{ .Values.tls.cert | b64enc }}
{{- end -}}
Environment-specific
{{- if eq .Values.env "prod" -}}
resources:
requests:
cpu: "2"
memory: "4Gi"
{{- else -}}
resources:
requests:
cpu: "100m"
memory: "256Mi"
{{- end -}}
Common findings this catches
nil pointer evaluating interface→ missing values; adddefaultorifguard.- Output has extra blank lines → missing
{{--}}whitespace control. - Inside
with, can’t access root values → use$.Values.... - Sub-chart values not applied → wrong nesting in parent values.yaml.
--set foo.bar=bazdoesn’t overridevalues.yaml→ it does, but priority is correct; double-checkvalues.yamlpath.- Multiline string broken in YAML output → use
nindentand proper string formatting. helm templatesucceeds buthelm installfails → admission webhook rejecting; use--dry-run.
When to escalate
- Complex sub-chart interactions — refactor; consider library charts for shared logic.
- GitOps tool (ArgoCD/Flux) shows drift after every sync — mutating admission webhook changing resources post-render.
- Performance: charts with 1000+ resources render slow — split into sub-charts.
Related prompts
-
Helm Chart Review Prompt
Get a senior-engineer review of a Helm chart — values hygiene, template correctness, security defaults, upgrade safety.
-
Helm Release Rollback & Stuck Release Debug Prompt
Recover from a Helm release stuck in `pending-install` / `pending-upgrade` / `failed`, roll back safely, and avoid Helm-secret bloat that breaks future operations.
-
Kubernetes Deployment Rollout Debug Prompt
Diagnose stuck Deployment rollouts — `ProgressDeadlineExceeded`, replica set churn, maxSurge/maxUnavailable misconfig, image pull pacing, and stuck-mid-rollout recovery.