Skip to content
LatchkeyLatchkey home

kubectl diff: Preview Changes Before Apply

kubectl diff compares your manifests against the live objects and prints a unified diff, exiting 1 when there is a difference.

Running diff before apply turns a deploy review into a real preview: you see exactly which fields the cluster would change.

What it does

kubectl diff sends your manifests to the API server in dry-run mode, retrieves what the merged objects would look like, and prints a diff against the current live state. The exit code is the gate: 0 means no changes, 1 means changes exist, and anything above 1 is an error.

Common usage

Terminal
kubectl diff -f deploy.yaml
kubectl diff -k ./overlays/prod
# gate a pipeline: succeed only if there are no drifts
kubectl diff -f deploy.yaml && echo "no changes"
# use server-side merge semantics
kubectl diff --server-side -f deploy.yaml

Options

Flag / EnvWhat it does
-f, --filenameManifests to compare
-k, --kustomizeDiff a kustomize directory
--server-sideUse Server-Side Apply merge for the comparison
--field-manager=<name>Field manager to attribute the dry-run apply
KUBECTL_EXTERNAL_DIFFOverride the diff program (e.g. colordiff)

In CI

Mind the exit code: diff returns 1 when there are changes, which set -e treats as failure. Capture it deliberately (for example kubectl diff ... || true to just print, or check $? -eq 1 to detect drift). Set KUBECTL_EXTERNAL_DIFF=diff so output is stable on minimal images.

Common errors in CI

"error: unable to recognize ...: no matches for kind" means a CRD in the manifest is not installed on the cluster, so diff cannot dry-run it; install the CRD first. A non-zero exit that you did not expect is usually just the "changes exist" signal (exit 1), not an error. "exec: \"diff\": executable file not found" appears on slim images without diffutils; install it or set KUBECTL_EXTERNAL_DIFF.

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

Frequently asked questions

kubectl diff: Preview Changes Before Apply?
Running diff before apply turns a deploy review into a real preview: you see exactly which fields the cluster would change.
What it does?
kubectl diff sends your manifests to the API server in dry-run mode, retrieves what the merged objects would look like, and prints a diff against the current live state. The exit code is the gate: 0 means no changes, 1 means changes exist, and anything above 1 is an error.
In CI?
Mind the exit code: diff returns 1 when there are changes, which set -e treats as failure. Capture it deliberately (for example kubectl diff ... || true to just print, or check $? -eq 1 to detect drift). Set KUBECTL_EXTERNAL_DIFF=diff so output is stable on minimal images.
Common errors in CI?
"error: unable to recognize ...: no matches for kind" means a CRD in the manifest is not installed on the cluster, so diff cannot dry-run it; install the CRD first. A non-zero exit that you did not expect is usually just the "changes exist" signal (exit 1), not an error.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card