Ephemeral, isolated kube-context subshells. One shell, one context, deleted on exit.
Run kush prod and you drop into a normal shell pinned to the prod context through a private, throwaway kubeconfig. Exit that shell and the kubeconfig is deleted; nothing about the session persists and ~/.kube/config is never touched. Open a prod shell in one terminal and a dev shell in another, and neither can bleed into the other or into your default kubectl setup.
Full documentation & getting started →
kubectl config use-context mutates a single shared file. Switch context in one terminal and every other terminal, script, and background tool reading ~/.kube/config switches with it. That's how you end up running a command against production because some other pane changed context five minutes ago.
kush sidesteps this by never touching that file. Each shell gets its own minimal kubeconfig scoped to exactly one context, so there's no global state to corrupt and no wrong-cluster surprise waiting for you.
It's also auth-agnostic: kush copies the user block from your existing kubeconfig verbatim, so exec plugins, OIDC, and cloud auth keep working unchanged. That's why it works with OpenShift with no extra setup: oc login, then kush <context>, and oc honors the pinned KUBECONFIG just like kubectl (see the OpenShift guide). Ships as a single Go binary with no runtime dependencies.
kush is unix-only in v1 (no Windows, no cmd.exe/PowerShell).
# Krew
kubectl krew index add kush https://github.com/SpechtLabs/kush.git
kubectl krew install kush/kush
# Homebrew (macOS/Linux)
brew install spechtlabs/tap/kush
# or build from source
go install github.com/spechtlabs/kush/cmd/kush@latest
# or run directly with Nix (Flakes)
nix run github:spechtlabs/kush -- versionOr grab a prebuilt binary from the releases page.
Verify:
kubectl kush versionIf you have fzf on your PATH, kush uses it for the context picker automatically; otherwise it falls back to a built-in TUI. Nothing to configure either way.
The whole workflow is enter, work, exit:
kubectl kush prod # subshell pinned to prod, via a private kubeconfig
kubectl get pods # kubectl, helm, k9s, flux: all see only prod
kubectl kush ns kube-system # re-pin the namespace in place, same shell
exit # temp kubeconfig deleted, back to your normal shellRun kubectl kush or kush with no argument to pick a context interactively. Krew installs the command as kubectl kush, and Homebrew, go install, and release binaries install it as kush. To switch context, you exit and enter another; kush deliberately won't change context in place, since that's the silent state change that gets people into trouble.
For scripts, CI, and agents that need a single command without an interactive shell, use kush exec. It pins KUBECONFIG for just that one process, forwards stdin/stdout/stderr, propagates the exit code, and cleans up when the command finishes:
kush exec prod -- kubectl get pods
kush exec prod -n kube-system -- kubectl get pods| Command | What it does |
|---|---|
kubectl kush [ctx] / kush [ctx] / kush ctx [name] |
Enter an isolated subshell for a context. No argument opens the picker. -l lists contexts and exits. |
kubectl kush ns [name] / kush ns [name] |
Re-pin the namespace of the current kush shell in place (or spawn a shell for your current context, if run outside one). |
kubectl kush exec <ctx> -- <cmd> / kush exec <ctx> -- <cmd> |
Run one command against an isolated context, non-interactively. -n sets the namespace. |
kubectl kush current / kush current |
Print the active context/namespace. Empty and exit 0 outside a kush shell. |
kubectl kush lint / kush lint |
Check every discovered kubeconfig for missing clusters/users, empty current-context, unreachable files. Exits non-zero on errors. |
kubectl kush split [-o dir] / kush split [-o dir] |
Write one self-contained kubeconfig per context to disk. Never mutates the source. |
kubectl kush init <bash|zsh|fish> / kush init <bash|zsh|fish> |
Emit optional prompt glue that shows a (kush:<ctx>) marker. |
kubectl kush completion <bash|zsh|fish> / kush completion <bash|zsh|fish> |
Standard shell completion for context names. |
kubectl kush version / kush version |
Print the version. |
Full flags and examples live in the CLI reference.
Config is optional; absent means sane defaults. kush reads config.yaml from ~/.config/kush/, $XDG_CONFIG_HOME/kush/, or /etc/kush/.
# ~/.config/kush/config.yaml
# Where kush looks for kubeconfigs, in precedence order.
# Omit to use the default: $KUBECONFIG + ~/.kube/config.
context_lookup_locations:
- $KUBECONFIG
- ~/.kube/config
- ~/.kube/configs/* # globs and $ENV/~ expansion both work
picker: auto # auto | builtin | fzf (env: KUSH_PICKER)
shell: "" # empty = $SHELL, then /bin/bash (env: KUSH_SHELL)
pre_exec_hook: "tsh join $KUSH_CONTEXT"
contexts:
cluster-123:
pre_exec_hook: "tsh join cluster-123"Inside every subshell, kush sets KUSH_ACTIVE, KUSH_CONTEXT, KUSH_NAMESPACE, and KUSH_KUBECONFIG, plus KUBECONFIG itself pointed at the temp file. That last one is what makes every kube-aware tool honor the isolation without any kush-specific flag. See the configuration reference for the details.
When you enter a context, kush extracts just that context's cluster and user into a fresh temp kubeconfig, points KUBECONFIG at it, and forks your shell. On exit (normal, signal, or crash) the temp file is deleted, and a stale-file sweep reaps anything a kill -9 might have left behind. Your real kubeconfig is only ever read, never written. The isolation writeup covers the mechanics.
Apache 2.0. See LICENSE.