Originally published on kuryzhev.cloud
One innocent kubectl delete in the wrong terminal tab — and suddenly you're explaining a prod outage caused by a kubectl wrong cluster context you forgot you switched an hour ago. This isn't a theoretical scenario. I've watched it happen to a senior engineer with eight years of Kubernetes under his belt, and I've done a milder version of it myself. The command runs fine. The apply succeeds. The problem is it succeeded on the wrong cluster.
Symptoms
You run kubectl apply or kubectl delete and it returns success — no errors, no warnings. But the resources show up in the wrong namespace, or worse, nothing changes where you expected. A minute later a teammate pings you: "did you just delete something in staging-eu?" You didn't think you were anywhere near staging-eu.
Sometimes the tell is quieter. You run kubectl get pods and get an empty list, or a set of pod names you don't recognize. Your brain assumes the cluster is broken. It isn't — you're just looking at the wrong one.
The postmortem version of this symptom is the worst: you run kubectl config current-context after the fact, during incident review, and it shows a cluster name nobody on the call remembers switching to. That's usually the moment the room goes quiet.
Root cause
Here's the part that trips people up even after years of using Kubernetes: kubectl context is global per kubeconfig file, not per terminal tab. If you switch context in one shell, and another open shell is reading the same ~/.kube/config, that other shell's "current" cluster just changed too — silently, with no notification.
Most engineers mentally model context as scoped to their terminal window. It isn't. It's scoped to the file. Two tabs, one file, one shared state. Switch in tab A, tab B is now pointed somewhere else, and tab B has no idea anything changed.
It gets murkier with KUBECONFIG pointing at multiple merged files. Run this to see how merging actually resolves:
KUBECONFIG=~/.kube/config:~/.kube/staging-config kubectl config view --merge
The merge order determines which context "wins" on a naming conflict. kubectl config get-contexts will show you the context flagged with an asterisk as current — always check this before scripting against it, because it's not necessarily the one you last explicitly set.
Then there's the muscle-memory problem. If you've aliased kubectl to k without also building in a context check, you've removed the one moment of friction that would normally make you pause and notice you're pointed at the wrong cluster. Aliases make commands faster. They also make mistakes faster.
Fix #1
The cheapest fix costs one command. Before any destructive action, run:
kubectl config current-context
# or, for scripting-safe output:
kubectl config view --minify -o jsonpath='{.current-context}'
Make this a reflex, not a courtesy. For anything risky — a delete, a scale-down, a rollout restart — pass --context explicitly instead of trusting the ambient default:
kubectl --context=staging-eu-gke delete pod flaky-worker-7d9f
This one flag removes ambiguity entirely for that single command, regardless of what the shared kubeconfig currently thinks is active. Also run kubectl config get-contexts periodically — it's the fastest way to spot duplicate cluster names from merged kubeconfigs, which is a common source of confusion when two teams generate configs with generic names like cluster1.
Gotcha: pairing --context without also setting --namespace (or -n) explicitly is a half-fix. You can be on the right cluster and still nuke the wrong namespace because it fell back to whatever default happens to be in that context.
Fix #2
The structural fix is making context switching scoped to a session, not the whole machine. Install kubectx and kubens, or better yet, kubie, which spawns an isolated subshell per context instead of mutating the shared file's default.
That distinction matters. kubectx switches the current context inside the same kubeconfig file everyone's shells are reading — it's faster than raw kubectl config use-context, but it's still a global mutation. kubie gives each terminal its own scoped context that doesn't leak to other tabs. If you regularly juggle prod and staging in parallel windows, I stopped using plain kubectx for that reason and moved to kubie ctx — the isolation is worth the extra tool.
Second habit: put the active context and namespace in your prompt. kube_ps1 or Starship's Kubernetes module will render it automatically. Once it's visible on every line, "I didn't notice I was on prod" stops being a valid excuse.
Third, if you're generating kubeconfigs for a team, name contexts unambiguously at creation time: prod-eks-us-east, not cluster1 or main. Generic names are how two engineers end up confidently pointed at different clusters while both typing the same command.
Fix #3
Sometimes the right move is putting friction back deliberately. Wrap kubectl in a shell function that demands typed confirmation for destructive verbs on anything matching a prod pattern:
# guard.sh — wrap kubectl to force confirmation on prod-like contexts
kubectl() {
local ctx
ctx=$(command kubectl config current-context 2>/dev/null)
# only guard destructive verbs
case "$1" in
delete|apply|scale|rollout|patch)
if [[ "$ctx" == prod-* ]]; then
read -p "Context is '$ctx'. Type the context name to confirm: " confirm
if [[ "$confirm" != "$ctx" ]]; then
echo "Aborted -- confirmation did not match context." >&2
return 1
fi
fi
;;
esac
command kubectl "$@"
}
# usage: source this in your shell rc file
# any teammate running `kubectl delete pod x` on prod-eks-us-east
# now has to type "prod-eks-us-east" to proceed
Pair that with per-project kubeconfigs loaded through direnv. An .envrc that sets KUBECONFIG only inside a specific repo directory means prod credentials aren't loaded by default in every shell you open — only when you deliberately cd into that project.
Add a backstop that doesn't depend on the operator's local setup at all: admission policies via Kyverno or OPA/Gatekeeper that block delete/scale on namespaces labeled environment: production, regardless of who's holding the session. This is the layer that saves you when a laptop config is wrong and nobody caught it locally.
Prevention
None of this sticks if it's one person's discipline. Standardize on kubectx/kubie in onboarding docs — don't leave context hygiene to individual habit, because habits break under incident pressure exactly when you need them most.
Store prod kubeconfigs separately from everything else, and prefer short-lived, scoped credentials over static certs sitting in ~/.kube/config indefinitely. Cloud-native IAM auth plugins (aws eks get-token, GKE's gcloud auth plugin) rotate tokens automatically and reduce the blast radius if a laptop or dotfiles repo leaks. A single bundled kubeconfig with cluster-admin creds for prod and staging together is a bigger security problem than most teams admit — scope RBAC per cluster instead of handing out the master key.
In CI/CD, never let a pipeline inherit context from a runner's cached kubeconfig. Pass it explicitly: kubectl apply --context=$TARGET_CLUSTER, sourced from a pipeline variable that's validated against the expected environment tag before the deploy step runs. Fail the job if they don't match.
Run through this before any manual kubectl session, especially during an incident when adrenaline overrides caution:
# Context-safety checklist -- run through before any manual kubectl command
[ ] `kubectl config current-context` matches the environment I intend
[ ] Namespace explicitly set with -n, not relying on default
[ ] Using kubectx/kubie subshell, not raw context switch in shared file
[ ] Prod credentials loaded only in a direnv-scoped shell, not globally
[ ] Destructive verb (delete/scale/patch) -- confirmation guard triggered?
[ ] CI pipeline passes --context explicitly, not inherited from runner cache
[ ] Kubeconfig for this cluster uses scoped RBAC, not cluster-admin
The kubectl wrong cluster context problem is cheap to prevent and expensive to clean up. Five seconds of friction — a confirmation prompt, a visible prompt segment, an explicit flag — is nothing compared to hours of on-call escalation after an accidental prod delete. See the full Kubernetes docs on configuring access to multiple clusters for the underlying mechanics, and check out more operational runbooks over on kuryzhev.cloud if this kind of incident writeup is useful to you.
Top comments (0)