kctx — a single-binary replacement for kubectx and kubens that fixes the three things which make them risky once you have more than a couple of clusters: a current-context every terminal shares, no guard rail in front of production, and an external fzf dependency for the picker.
For detailed documentation, see the docs/ folder:
Usage | Configuration | Examples | Use Cases | Deployment | Development
kubectx + kubens |
kctx |
|
|---|---|---|
| Scope of a switch | Global — every terminal follows | Per-terminal via the shell hook or kctx shell, global otherwise |
| Production guard | None | Contexts — and the namespaces inside them — classified by name, exact list, prefix or suffix; colored everywhere, and confirm gates ctx, ns, shell, exec and export alike |
| Context per project | — | kctx bind: cd into a repo, this terminal follows |
| Interactive picker | Needs fzf on $PATH |
Built in — no external dependency (not on Windows yet, where kctx prints the list) |
| Run against another cluster | Switch, run, switch back | kctx exec prod -- kubectl get pods |
| The same question, every cluster | A for loop that switches and hopes |
kctx exec --all -- kubectl get nodes, in parallel |
| Namespace list offline | Fails when the API server is unreachable | Falls back to a cache and says so |
| Cluster health | — | kctx doctor: reachability, version, expired certs and tokens |
| Certificates about to expire | — | kctx expiry: every cluster's TLS secrets and cert-manager Certificates in one table |
| Adding someone's kubeconfig | Hand-merge, or --flatten and hope the names do not clash |
kctx import: colliding stanzas are disambiguated, never overwritten |
| Handing one context over | Edit a copy by hand | kctx export prod --flatten -f prod.yaml |
| Backups | — | Automatic before every destructive kubeconfig edit |
| Binaries | Two | One |
A kubeconfig has exactly one current-context. Switch it in one tab and every other tab follows — including the one where you are halfway through a production incident. kctx gives each terminal its own copy of the kubeconfig and points $KUBECONFIG at it:
# terminal A # terminal B
$ kctx ctx prod-eks $ kubectl config current-context
Switched to context prod-eks. dev-cluster # unchangedThat behavior needs a shell hook, because a child process cannot change its parent's environment. One line in your rc file installs it:
eval "$(kctx init zsh)" # or bash; fish: kctx init fish | sourceWithout the hook, kctx edits the global kubeconfig exactly the way kubectx does — nothing breaks, you just do not get the isolation.
# Homebrew (macOS / Linux)
brew install somaz94/tap/kube-ctx
# Scoop (Windows)
scoop bucket add somaz94 https://github.com/somaz94/scoop-bucket
scoop install kube-ctx
# krew (kubectl plugin)
kubectl krew index add somaz94 https://github.com/somaz94/krew-index
kubectl krew install somaz94/ctx2
# Install script
curl -fsSL https://raw.githubusercontent.com/somaz94/kube-ctx/main/scripts/install.sh | bash
# From source
git clone https://github.com/somaz94/kube-ctx.git
cd kube-ctx && make build && make install # → /usr/local/bin/kctxPrebuilt archives without the install script: Deployment → Binary.
Then, for per-terminal isolation, one line in your rc file:
eval "$(kctx init zsh)" # bash: kctx init bash; fish: kctx init fish | sourceOn the krew install, the binary is named
kubectl-ctx2, and switching throughkubectl ctx2 ctx ...is always global —kubectlruns a plugin as a subprocess, which cannot change the shell that called it. Install the hook againstkubectl-ctx2and call it directly for shell-local switching. See Deployment.
kctx # (via ctx) interactive picker
kctx prod-eks # switch, the way kubectx takes it
kctx ctx prod-eks # the same thing, spelled out
kctx - # back to the previous context
kctx ns kube-system # namespace of the current context
kctx list --wide # everything at a glance
kctx doctor # what still works?
kctx expiry # what runs out in the next 30 days?
kctx exec prod-eks -- kubectl get nodes # one command, no switch
kctx exec --all -- kubectl get nodes # every cluster, in parallel
kctx bind dev # cd here later, and this terminal follows
kctx shell prod-eks # a subshell pinned to prod| Command | What it does |
|---|---|
kctx [name|-|-N] |
Switch context. No argument opens the picker; - goes back |
kctx ctx [name|-|-N] |
The same, spelled out — and the way to reach a context named like a subcommand |
kctx ns [name|-|-N] |
Switch the namespace of the current context |
kctx current [-n] |
Print the current context (or namespace) and exit — for prompts |
kctx list [--wide] |
Table of every context, with guard badges |
kctx rename <old> <new> |
Rename a context (. = current) |
kctx delete|rm|del <name>... [--prune] |
Delete contexts, optionally their orphaned cluster/user entries |
kctx import <file>... [--prune] |
Merge contexts from another kubeconfig, without colliding |
kctx export [name]... [-f file] |
Write contexts out as a standalone kubeconfig |
kctx alias <name> <context> |
Short names usable anywhere a context is |
kctx bind [context] |
Bind a directory to a context; cd there switches |
kctx guard add|list|remove |
Classify a context — or a namespace inside it — as production, without writing a regex |
kctx doctor [context...] |
Parallel health check; non-zero exit if anything is broken |
kctx expiry|expire|certs [context...] |
What TLS certificates expire soon, across every cluster; exit 2 inside the window, or when a cluster could not be read |
kctx shell [context] |
Subshell pinned to a context |
kctx sessions [--clean] |
List the per-terminal kubeconfig copies, and tidy them |
kctx exec <context> -- <cmd> |
Run one command against a context |
kctx exec --all|-c a,b -- <cmd> |
Run it against many contexts at once |
kctx init bash|zsh|fish |
Shell hook + completions |
kctx version |
Build information |
Global flags: --kubeconfig, -o color|plain|json, --no-color, -y/--yes. An unknown -o value is an error rather than a silent fallback, so a script asking for -o jsno never gets a human table to parse.
Exit status is scriptable: 1 is kube-ctx failing, 2 is doctor finding a sick cluster, or expiry finding a certificate inside the window or failing to read a cluster at all, 130 is you declining a prompt — so kctx ctx prod && ./deploy.sh does not deploy when you back out. See Usage.
Contexts are classified by name. Out of the box prod, prd and production are marked DANGER, staging/uat WARN — labels only, nothing blocks. Turn a rule into a real gate in ~/.config/kube-ctx/config.yaml:
guards:
- match: '(^|[-_.])(prod|production)([-_.]|$)'
level: danger
confirm: true # retype the context name to switch to it
aliases:
p: prod-eks-apne2$ kctx ctx prod-eks-apne2
! prod-eks-apne2 is classified danger by the guard rule (^|[-_.])(prod|production)([-_.]|$).
Type "prod-eks-apne2" to continue:
The gate covers kctx shell and kctx exec too, not just switching — otherwise kctx exec prod -- kubectl delete deploy/api would walk straight past it. Declining exits 130, so kctx ctx prod && ./deploy.sh does not deploy.
Names lie, though — the cluster that would hurt most to break is often the one called cluster-7. Name it directly, no regex and no editing:
kctx guard add cluster-7 --confirm --label PROD
kctx guard add --suffix -live --level danger
kctx guard listA rule can also list namespaces:, which moves it onto the other axis: it then guards those namespaces inside the contexts it matches, rather than the contexts themselves. That is the guard for someone who lives in a production cluster all day, where the risk is not arriving but what gets deleted in kube-system — and it covers kctx ctx, kctx ns, kctx exec -n and kctx shell -n alike:
kctx guard add --prefix prod- -n kube-system --confirmSee Configuration for every field.
Certificates run out on a schedule nobody is watching. kctx expiry reads every kubernetes.io/tls secret in every context, in parallel, and reports what falls inside the next 30 days:
$ kctx expiry --days 30
CONTEXT NAMESPACE KIND NAME IN
prod-eks istio Certificate gateway-cert 8d (auto)
prod-eks default Secret/tls legacy-api 19d
staging ingress Secret/tls wildcard 27d
The certificate is the unit, not whatever manages it — a secret pasted in by hand two years ago shows up next to a cert-manager Certificate. Where cert-manager is present its Certificates are folded in, which is what separates the rows that renew themselves ((auto), dimmed) from the ones somebody has to act on. This is deliberately not doctor: nothing here is broken yet, which is the point of being told. Exit 2 inside the window makes it a cron gate — kctx expiry --days 30 || notify-oncall. See Usage.
Someone sends you a kubeconfig. The usual answer is KUBECONFIG=a:b kubectl config view --flatten > merged, and it has a trap: every kubeadm cluster calls its cluster kubernetes and its user kubernetes-admin, so the merge resolves the collision by first-writer-wins and the contexts from the file listed second quietly land on the first file's API server. You find out later, from the wrong cluster.
$ kctx import ~/Downloads/kubeconfig.yaml
CONTEXT ACTION CLUSTER USER SOURCE
kubernetes-admin@kubernetes added kubernetes-2 kubernetes-admin-2
Imported 1 context(s). Switch to one with kctx ctx <name>.A stanza whose contents differ is never replaced — the incoming one lands under a name of its own and only the imported context points at it. A stanza identical to one already there is reused rather than duplicated, so re-running an import is a no-op instead of a pile of copies. Colliding context names are refused outright until you say what to do about them (--prefix, --as, --overwrite), and --dry-run shows the whole plan first.
The other direction hands one context to someone else, or takes a backup:
kctx export prod --flatten -f prod.yaml # certificates inlined, so it works elsewhere
kctx export --all -f backup.yaml # everythingExports are written 0600, never overwrite a file without --force, and a confirm guard applies — handing over a kubeconfig is handing over a route to the cluster. See Usage.
| Path | Contents |
|---|---|
$XDG_CONFIG_HOME/kube-ctx/config.yaml |
aliases, guard rules |
$XDG_STATE_HOME/kube-ctx/history* |
context and namespace history |
$XDG_STATE_HOME/kube-ctx/backups/ |
kubeconfig snapshots, 10 generations |
$XDG_STATE_HOME/kube-ctx/shells/ |
per-terminal kubeconfig copies (kctx sessions) |
$XDG_CACHE_HOME/kube-ctx/namespaces/ |
namespace list cache |
Everything is created 0600/0700 — the copies hold credentials. Nothing is written to ~/.kube/ except the kubeconfig edit you asked for, and destructive edits are backed up first.
- kube-diff: Diff local manifests (plain YAML, Helm, Kustomize) against the live cluster.
- kube-events: View and summarize Kubernetes events, grouped by resource with warnings first.
Apache 2.0 — see LICENSE.
