← Recipes

阅读中文版本

Diff Rendered Helm Manifests Before Applying Them

Render charts locally with helm template, diff against the cluster live state, and review the exact YAML that will change before it changes.

~25 mindeployhelmkubernetes

Task

Render the chart with production values, compare against live cluster state, and review every difference before applying.

When to use this

A values.yaml tweak was supposed to change one replica count. The upgrade instead rewrote thirty annotations nobody expected, and prod drained for a restart storm.

Prerequisites

  • Helm 3.9 or newer with helm template and helm diff plugin optional
  • kubectl read access to the target namespace
  • The chart and production values file locally

Steps

1. Render the chart to disk with the exact production values.

helm template web ./charts/web -f values.yaml -f values-prod.yaml --namespace prod > /tmp/rendered.yaml && grep -c "kind:" /tmp/rendered.yaml

Expected output: The rendered file contains all resources with a count printed, giving a static artifact safe to inspect and diff.

2. Compare rendered manifests against the live cluster state.

for r in $(grep -E "^kind:" /tmp/rendered.yaml | awk '{print $2}' | sort -u); do echo "== $r =="; done; kubectl diff -f /tmp/rendered.yaml 2>&1 | head -40

Expected output: kubectl diff prints additions, deletions, and modifications in patch format, enumerating exactly what applying would change.

3. Sanity-check values interpolation for accidental blanks.

grep -nE ":( null| ")" /tmp/rendered.yaml | grep -vE "creationTimestamp|clusterIP" | head -10; grep -nE "\"\$\{|{{" /tmp/rendered.yaml

Expected output: Unrendered template braces or null-valued fields print, catching missing values that would deploy as empty strings.

4. Review the resource that triggered the whole change.

awk '/^kind: Deployment$/,/^---/' /tmp/rendered.yaml | grep -A2 -E "replicas:|image:|resources:"

Expected output: The replica count, image, and resources of the Deployment print, confirming the one intended change is present and correct.

5. Apply only after the diff is reviewed, and keep the rendered artifact.

helm upgrade --install web ./charts/web -f values.yaml -f values-prod.yaml -n prod --atomic --timeout 5m && cp /tmp/rendered.yaml releases/web-$(date +%F).yaml

Expected output: The upgrade applies with automatic rollback on failure, and the reviewed artifact is archived next to the release for future audits.

Troubleshooting

Problem

helm template output differs from what helm install applies

Solution

Post-rendering hooks or capabilities differ; pass the same --namespace and set flags to template, and verify against helm get manifest for the running release.

Problem

kubectl diff exits with error on CRDs

Solution

Custom resources lack established schemas in the cluster; filter them into a separate file and review that file manually, applying CRDs before workload manifests.

Problem

Secrets render as empty strings

Solution

The values file references an external secret not present locally; switch the template to look up secrets at apply time or inject them via --set-file during the real upgrade only.

Limitations

  • Rendered diffs show desired-vs-live state only; hooks execute at apply time and their side effects are invisible to template.
  • Large charts make full diffs noisy; scope reviews to the resources your values touched, using the resource-type filter.

Review the YAML, not the intention

A one-line values change can cascade into dozens of manifest edits through templating logic. Rendering the chart and diffing it against the live cluster shows the exact YAML that will change, turning a hopeful upgrade into a reviewed one.

# Render with the exact production inputs
helm template web ./charts/web \
  -f values.yaml -f values-prod.yaml \
  --namespace prod > /tmp/rendered.yaml

# What would actually change?
kubectl diff -f /tmp/rendered.yaml | head -40
# -  replicas: 3
# +  replicas: 5        <-- intended
# +  annotations: {"checksum/config": "b7c1..."}  <-- restart storm

# Then apply with a safety net
helm upgrade --install web ./charts/web \
  -f values.yaml -f values-prod.yaml \
  -n prod --atomic --timeout 5m

The checksum annotation is the restart storm

Charts commonly stamp a config checksum into the pod template so config changes roll pods. When your values rewrite also rewrites the config map, every pod restarts even though the config content is logically identical. Seeing it in the diff lets you decide whether the restart is acceptable at this hour.

Archive the artifact you reviewed

Keep the rendered YAML per release. When something breaks days later, comparing two rendered files answers what changed far faster than archaeology across chart versions, values files, and template logic.