# kubectl diff: Usage, Options & Common CI Errors

> kubectl diff previews what an apply would change against the live cluster. Using it as a CI gate, its exit codes, and the RBAC and server-side diff errors.

Source: https://latchkey.dev/learn/command-reference/kubectl-diff  
Updated: 2026-06-25

See exactly what an apply will change - before it changes it.

kubectl diff compares your manifests against the live objects and prints the differences, using the same server-side merge apply would. It is the safety check to run before a deploy and a useful PR artifact.

## What it does

kubectl diff -f sends your manifest to the API server in dry-run mode, gets back the would-be result, and diffs it against the current state. It exits 0 when there is no diff, 1 when there is a diff, and >1 on error - handy for gating. It reflects defaulting and admission mutations, so it shows the real outcome, not just a text diff.

## Common usage

```Terminal
kubectl diff -f manifests/
kubectl diff -k overlays/prod
kubectl diff -f deploy.yaml || echo "changes pending"
KUBECTL_EXTERNAL_DIFF="colordiff" kubectl diff -f deploy.yaml
```

## Common errors in CI

The exit-code semantics trip up scripts: a pipeline that runs set -e treats diff's exit 1 (there is a diff) as a failure and aborts. Either tolerate exit 1 explicitly (kubectl diff -f x || true) or branch on it. "forbidden: User cannot patch resource" during diff means the service account lacks the dry-run/patch permission the diff needs. Diff requires the same RBAC as apply. And like apply, server-side diff needs a reachable API server; an unreachable cluster gives a connection error, not an empty 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
```

> 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

### kubectl diff: Usage, Options & Common CI Errors?

kubectl diff compares your manifests against the live objects and prints the differences, using the same server-side merge apply would. It is the safety check to run before a deploy and a useful PR artifact.

### What it does?

kubectl diff -f sends your manifest to the API server in dry-run mode, gets back the would-be result, and diffs it against the current state. It exits 0 when there is no diff, 1 when there is a diff, and >1 on error - handy for gating. It reflects defaulting and admission mutations, so it shows the real outcome, not just a text diff.

### Common errors in CI?

The exit-code semantics trip up scripts: a pipeline that runs set -e treats diff's exit 1 (there is a diff) as a failure and aborts. Either tolerate exit 1 explicitly (kubectl diff -f x || true) or branch on it.

---

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
