Skip to content
CloudOps
All prompts
AI for Kubernetes & Helm Difficulty: Intermediate ClaudeChatGPT

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

  1. Run helm template --debug before guessing.
  2. Verify values precedence — list every source.
  3. For scope issues, remember $ is root; . follows with/range.
  4. 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; add default or if guard.
  • 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=baz doesn’t override values.yaml → it does, but priority is correct; double-check values.yaml path.
  • Multiline string broken in YAML output → use nindent and proper string formatting.
  • helm template succeeds but helm install fails → 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

Newsletter

Get weekly AI workflows for DevOps engineers

Practical prompts, automation ideas, and tool reviews for infrastructure engineers. One email per week. No spam.