infra/.claude/skills/helm-release-force-rerender/SKILL.md
Viktor Barzin a26fdd27b2 [ci skip] Add skills: traefik-http3-quic and helm-release-force-rerender
- traefik-http3-quic: Enable HTTP/3 (QUIC) on Traefik with advertisedPort
  gotcha, Cloudflare zone settings, and testing instructions
- helm-release-force-rerender: Fix Helm releases where Terraform applies
  but K8s resources don't reflect new values (state rm + reimport pattern)
2026-02-07 20:49:34 +00:00

6.5 KiB

name description author version date
helm-release-force-rerender Fix for Helm releases managed by Terraform where changing Helm values doesn't update the actual Kubernetes resources. Use when: (1) Terraform applies successfully but K8s resources (Service, Deployment) don't reflect new Helm values, (2) New ports/volumes/containers from Helm chart values don't appear in the deployed resources, (3) helm upgrade --reuse-values doesn't re-render templates for structural changes, (4) Terraform thinks Helm release is up-to-date but actual K8s resources are stale. Solution involves removing from Terraform state, reimporting, and force upgrading. Claude Code 1.0.0 2026-02-07

Helm Release Force Re-render via Terraform

Problem

After changing Helm chart values in a Terraform helm_release resource, Terraform applies successfully but the actual Kubernetes resources (Services, Deployments, etc.) don't reflect the new values. For example, adding a new port in Helm values doesn't result in that port appearing in the Service spec.

Context / Trigger Conditions

  • Terraform helm_release applies with "1 changed" but kubectl get svc -o yaml shows the old configuration
  • Structural changes to Helm values (new ports, new containers, new volumes) are not reflected in deployed resources
  • The Helm chart templates need to be fully re-rendered, not just patched
  • Common with Traefik, ingress-nginx, and other charts where template logic conditionally includes resources based on values

Root Cause

Terraform's helm_release resource uses helm upgrade under the hood. When values are changed, Helm may use --reuse-values behavior where it merges new values into existing ones rather than doing a full template re-render. For structural changes (like enabling HTTP/3 which adds a new UDP port to the Service template), the templates may not be re-rendered with the new conditional branches active.

Additionally, Terraform may see the stored Helm release state as matching the desired state even though the actual Kubernetes resources don't reflect it, creating a state drift that Terraform doesn't detect.

Solution

Step 1: Verify the Discrepancy

Confirm that K8s resources don't match Helm values:

# Check the actual resource
kubectl get svc <service-name> -n <namespace> -o yaml

# Check what Helm thinks is deployed
helm get values <release-name> -n <namespace>
helm get manifest <release-name> -n <namespace> | grep -A10 "<expected-config>"

Step 2: Remove Helm Release from Terraform State

terraform state rm 'module.kubernetes_cluster.module.<service>.helm_release.<name>'

IMPORTANT: This only removes from Terraform state. The actual Helm release and K8s resources remain untouched in the cluster.

Step 3: Import the Helm Release Back

terraform import 'module.kubernetes_cluster.module.<service>.helm_release.<name>' '<namespace>/<release-name>'

For Helm releases, the import ID format is namespace/release-name.

Step 4: Force Apply with Terraform

After reimporting, run terraform apply. Terraform should now detect the drift between the desired Helm values and the actual release state:

terraform apply -target=module.kubernetes_cluster.module.<service>

If Terraform still shows "no changes", you may need to taint the resource:

terraform taint 'module.kubernetes_cluster.module.<service>.helm_release.<name>'
terraform apply -target=module.kubernetes_cluster.module.<service>

Step 5: Manual Helm Force Upgrade (Last Resort)

If Terraform still doesn't fix it, use Helm directly as a one-time fix, then reimport:

# Get the current values file
helm get values <release-name> -n <namespace> -o yaml > /tmp/values.yaml

# Edit /tmp/values.yaml to include the correct values, or use --set flags

# Force upgrade (re-renders all templates)
helm upgrade --force <release-name> <chart> -n <namespace> -f /tmp/values.yaml

# Then reimport into Terraform
terraform state rm 'module.kubernetes_cluster.module.<service>.helm_release.<name>'
terraform import 'module.kubernetes_cluster.module.<service>.helm_release.<name>' '<namespace>/<release-name>'
terraform apply -target=module.kubernetes_cluster.module.<service>

WARNING: Direct Helm operations bypass Terraform. Always reimport into Terraform state afterward, and use terraform apply to verify Terraform is back in sync.

Verification

# Check the K8s resources now match expected configuration
kubectl get svc <service-name> -n <namespace> -o yaml
kubectl get deployment <deployment-name> -n <namespace> -o yaml

# Verify Terraform is in sync
terraform plan -target=module.kubernetes_cluster.module.<service>
# Should show "No changes" or minimal expected drift

Example: Traefik HTTP/3 UDP Port Not Appearing

Problem: Added http3.enabled=true to Traefik Helm values. Terraform applied successfully, but the Traefik Service only had TCP port 443, missing the expected UDP port 443 (websecure-http3).

Fix:

# 1. Remove from state
terraform state rm 'module.kubernetes_cluster.module.traefik.helm_release.traefik'

# 2. Reimport
terraform import 'module.kubernetes_cluster.module.traefik.helm_release.traefik' 'traefik/traefik'

# 3. Apply (Terraform now detects the drift)
terraform apply -target=module.kubernetes_cluster.module.traefik

# 4. Verify
kubectl get svc traefik -n traefik -o yaml | grep -A3 "websecure-http3"
# Should show: port: 443, protocol: UDP

Notes

  • This issue is more common with structural Helm value changes (new ports, new sidecars, conditional template blocks) than with simple value changes (image tags, replica counts)
  • The helm upgrade --force flag deletes and recreates resources that have changed, which causes brief downtime. Use with caution on production ingress controllers.
  • Always verify with terraform plan after fixing to ensure Terraform state is consistent
  • This is different from the terraform-state-identity-mismatch skill, which covers provider-level identity errors. This skill covers Helm template rendering issues where the state looks correct but the actual resources don't match.

See Also

  • terraform-state-identity-mismatch - For Terraform provider identity errors
  • traefik-http3-quic - For enabling HTTP/3 on Traefik (common trigger for this issue)

References