helmfile diff: Preview Release Changes
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
helmfile -e production diff
# fail-on-change gate for a PR
helmfile diff --detailed-exitcode --suppress-secretsOptions
| 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.
# 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