# helmfile diff: Preview Release Changes

> helmfile diff shows what helmfile apply would change. Reference for --detailed-exitcode, --suppress-secrets, --context, and using diff as a CI gate.

Source: https://latchkey.dev/learn/command-reference/helmfile-diff  
Updated: 2026-06-30

helmfile diff renders every release and shows the changes a sync or apply would make, without changing the cluster.

diff is the plan step for helmfile. Run it on a pull request to show reviewers the exact Kubernetes changes a deploy would produce.

## What it does

helmfile diff uses the helm-diff plugin to render each release and compare it against the live cluster, printing a unified diff of the manifests. With --detailed-exitcode it returns 2 when there are changes, which is useful for gating.

## Common usage

```Terminal
helmfile -e production diff
# fail-on-change gate for a PR
helmfile diff --detailed-exitcode --suppress-secrets
```

## Options

| Flag | What it does |
| --- | --- |
| --detailed-exitcode | Exit 2 when there are changes (0 if none) |
| --suppress-secrets | Redact Secret values in the diff |
| --context <n> | Lines of context around each change |
| --selector key=value | Diff only matching releases |

## In CI

With --detailed-exitcode, exit 0 means no drift, 2 means changes, and 1 means an error, so a PR check can distinguish "nothing to do" from "this will change things". Always add --suppress-secrets so rendered Secret values do not leak into build logs.

## Common errors in CI

"Error: unknown command \"diff\" for \"helm\"" means the helm-diff plugin is missing; run helmfile init --force. A job failing with exit code 2 under --detailed-exitcode is not an error, it means changes were detected. "could not decrypt" appears when a release uses helm-secrets and the decryption key is unavailable.

## Using this in CI

A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.

```Terminal
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods

# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info

# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes
```

> Always set `--request-timeout` in CI. Without it an unreachable API server hangs until the job times out, which turns a thirty-second failure into a twenty-minute one.

## FAQ

### helmfile diff: Preview Release Changes?

diff is the plan step for helmfile. Run it on a pull request to show reviewers the exact Kubernetes changes a deploy would produce.

### What it does?

helmfile diff uses the helm-diff plugin to render each release and compare it against the live cluster, printing a unified diff of the manifests. With --detailed-exitcode it returns 2 when there are changes, which is useful for gating.

### In CI?

With --detailed-exitcode, exit 0 means no drift, 2 means changes, and 1 means an error, so a PR check can distinguish "nothing to do" from "this will change things". Always add --suppress-secrets so rendered Secret values do not leak into build logs.

### Common errors in CI?

"Error: unknown command \"diff\" for \"helm\"" means the helm-diff plugin is missing; run helmfile init --force. A job failing with exit code 2 under --detailed-exitcode is not an error, it means changes were detected. "could not decrypt" appears when a release uses helm-secrets and the decryption key is unavailable.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
