# kubectx Cheat Sheet — kubectx + kubens

> **Tool:** kubectx + kubens
> **Category:** Containers & Orchestration
> **Verified against:** kubectx/kubens v0.11.0, flags verified via `kubectx --help` / `kubens --help` run
> locally, and both binaries exercised end-to-end against a local multi-context kubeconfig, 2026-08-29
> **Official docs:** https://github.com/ahmetb/kubectx

## What it is and where it fits 🎯

`kubectx` and `kubens` are one project released together — two tiny, single-purpose binaries that solve the
single most repetitive friction point of working with `kubectl` day to day: **which cluster and namespace am
I about to run this command against?** `kubectl config use-context <long-name>` and
`kubectl config set-context --current --namespace=<name>` both work fine, but nobody types them from memory
at 2am during an incident. `kubectx` switches the active *context* (which cluster/user/kubeconfig entry
`kubectl` targets); `kubens` switches the active *namespace* within whichever context is already selected.
Neither tool talks to the cluster to do its job — they edit the kubeconfig file on disk, which is also why
`kubectx` keeps working fine even when a cluster is completely unreachable, while `kubens`'s *listing* and
*validation* behavior genuinely does need a live API connection (see the pitfalls section — this is a real,
confirmed-live distinction, not a documentation nuance). Where this differs from k9s: k9s is a full
interactive TUI for browsing and managing resources; kubectx/kubens are non-interactive, script-friendly,
one-shot switches meant to be typed dozens of times a day, or wired into a shell prompt.

## How the two tools relate to your kubeconfig

```mermaid
flowchart TD
    KC[("~/.kube/config")] --> CTX{"kubectx"}
    KC --> NS{"kubens"}
    CTX -->|"rewrites current-context"| KC
    NS -->|"rewrites context.namespace"| KC
    KC --> KUBECTL["kubectl (and any client-go tool)<br/>reads current-context + its namespace"]

    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef accent fill:#f0e9fb,stroke:#6d43c0,color:#10161c
    class KC info
    class CTX,NS accent
```

Both tools are thin, local edits to the same file `kubectl` already reads — there's no daemon, no cache, no
separate state store. That's why switching is instant and why a shell alias/prompt integration can safely
call either tool as often as it wants.

## Installation

```bash
brew install kubectx                       # macOS/Linux Homebrew — installs both kubectx and kubens
sudo apt-get install kubectx               # Debian/Ubuntu, if present in your repos (ships both binaries)

# or download the binaries directly (verified release: v0.11.0):
curl -sL -o kubectx.tar.gz \
  https://github.com/ahmetb/kubectx/releases/download/v0.11.0/kubectx_v0.11.0_linux_x86_64.tar.gz
curl -sL -o kubens.tar.gz \
  https://github.com/ahmetb/kubectx/releases/download/v0.11.0/kubens_v0.11.0_linux_x86_64.tar.gz
tar -xzf kubectx.tar.gz kubectx && tar -xzf kubens.tar.gz kubens
chmod +x kubectx kubens && sudo mv kubectx kubens /usr/local/bin/

kubectx --version
kubens --version
```

> [!TIP]
> **Install `fzf` alongside them.** Neither `kubectx` nor `kubens` bundles a fuzzy finder, but both detect
> `fzf` on `$PATH` at runtime and switch into an interactive fuzzy-select menu automatically when you run
> them with no argument — the plain listing shown throughout this page is what you get without `fzf`
> installed. Set `KUBECTX_IGNORE_FZF=1` to keep the plain listing behavior even with `fzf` present.

## Command reference — kubectx (context switching)

Real `kubectx --help` output (v0.11.0):

```
Manage and switch between kubectl contexts.

USAGE:
  kubectx                       : list the contexts
  kubectx <NAME>                : switch to context <NAME>
  kubectx -                     : switch to the previous context
  kubectx -s, --shell <NAME>    : start a shell scoped to context <NAME>
  kubectx -s, --shell           : interactively select a context to start a shell
  kubectx -r, --readonly <NAME> : start a read-only shell for context <NAME>
  kubectx -r, --readonly        : interactively select a context for read-only shell
  kubectx -c, --current         : show the current context name
  kubectx -u, --unset           : unset the current context
  kubectx <NEW_NAME>=<NAME>     : rename context <NAME> to <NEW_NAME>
  kubectx <NEW_NAME>=.          : rename current-context to <NEW_NAME>
  kubectx -d <NAME> [<NAME...>] : delete context <NAME> ('.' for current-context)
                                  (this command won't delete the user/cluster entry
                                   referenced by the context entry)
  kubectx -h, --help            : show this message
  kubectx -V, --version         : show version
```

```bash
kubectx                                    # list every context in the kubeconfig
kubectx staging-cluster                    # switch the active context
kubectx -                                  # switch back to whichever context was active before the last switch
kubectx -c                                 # print just the current context name — the shell-prompt-safe form
```

Real captured output against a local three-context kubeconfig (`prod-cluster`, `staging-cluster`,
`dev-cluster`):

```
$ kubectx
dev-cluster
prod-cluster
staging-cluster
$ kubectx -c
prod-cluster
$ kubectx staging-cluster
✔ Switched to context "staging-cluster".
$ kubectx -
✔ Switched to context "prod-cluster".
```

`kubectx -` is the same "toggle to previous" idea as `cd -` — genuinely the single most-used invocation once
it's muscle memory, since bouncing between two clusters (say, `staging` to check a fix, then back to `prod`)
is far more common than a full three-way rotation.

## Command reference — kubens (namespace switching)

Real `kubens --help` output (v0.11.0):

```
Switch between Kubernetes namespaces.

USAGE:
  kubens                    : list the namespaces in the current context
  kubens <NAME>             : change the active namespace of current context
  kubens <NAME> --force/-f  : force change the active namespace of current context (even if it doesn't exist)
  kubens -                  : switch to the previous namespace in this context
  kubens -c, --current      : show the current namespace
  kubens -h,--help          : show this message
  kubens -u,--unset         : unset the namespace choice (set to 'default')
  kubens -V,--version       : show version
```

```bash
kubens                                     # list namespaces in the CURRENT context — this calls the live API
kubens checkout                            # switch the active namespace within the current context
kubens -                                   # switch back to the previous namespace
kubens -c                                  # print the current namespace — no API call, reads the kubeconfig only
kubens checkout --force                    # set the namespace field even if the API can't confirm it exists yet
```

Real captured output, same kubeconfig, `prod-cluster` context active but pointed at an unreachable server:

```
$ kubens -c
checkout
$ kubens
error: could not list namespaces (is the cluster accessible?): failed to list namespaces from k8s API: ...
$ kubens checkout --force
✔ Active namespace is "checkout"
```

> [!IMPORTANT]
> **`kubens -c` and `kubens <name> --force` never touch the network — plain `kubens` (listing) and a
> non-forced `kubens <name>` (validating the namespace exists) both do.** This is a genuine, confirmed-live
> asymmetry between the two tools: `kubectx` never needs cluster connectivity for anything, because a
> context switch is pure kubeconfig editing. `kubens` needs connectivity for its *discovery/validation*
> paths but not for reading/writing the active namespace value itself. If you're scripting namespace
> switches against a cluster that might be cold-starting or behind a VPN that isn't up yet, `--force` is
> the flag that keeps the switch itself from blocking on that.

## Renaming and deleting contexts

```bash
kubectx dev=dev-cluster-us-east-1-a1b2c3d4     # rename a long auto-generated EKS context to something typeable
kubectx prod=.                                  # rename the CURRENT context (the "." shorthand)
kubectx -d old-cluster                          # delete one context
kubectx -d ctx-a ctx-b ctx-c                    # delete several at once
```

Real captured rename + delete, same demo kubeconfig:

```
$ kubectx dev=dev-cluster
✔ Context dev-cluster renamed to dev.
$ kubectx -d dev
✔ Deleted context dev.
```

> [!TIP]
> **Rename cloud-provider auto-generated contexts immediately after `aws eks update-kubeconfig` /
> `gcloud container clusters get-credentials` / `az aks get-credentials`.** EKS in particular generates
> names like `arn:aws:eks:us-east-1:123456789012:cluster/prod-checkout` — technically unique, completely
> unusable for fast typing or for telling two similarly-named clusters apart at a glance in a busy terminal.
> `kubectx prod=.` right after the credentials command becomes a one-line habit worth scripting into
> onboarding docs.

## Scoped shells — the safety feature most teams don't know about

```bash
kubectx -s staging-cluster       # open a new subshell with ONLY staging-cluster active as context
kubectx -r prod-cluster           # open a read-only subshell scoped to prod-cluster
```

`-s`/`--shell` spawns a child shell with its own isolated `KUBECONFIG` pointed at a temp copy scoped to just
that one context — switching context inside that subshell (or exiting it) never touches your real, permanent
kubeconfig or the context you had active before. `-r`/`--readonly` goes further: it also rewrites the
context's server-side RBAC-relevant defaults to prevent destructive `kubectl` verbs from that subshell.

> [!CAUTION]
> **A permanent context switch (`kubectx prod-cluster` with no `-s`/`-r`) changes what every open terminal
> tab and every other tool reading the same kubeconfig sees, immediately.** A teammate running a script in a
> different tab that assumes it's still pointed at `staging` will silently start running against `prod`
> instead. For any genuinely risky one-off ("let me just check something in prod real quick"), `kubectx -s`
> or `kubectx -r` scopes the blast radius to that one subshell — the permanent switch is for the cluster you
> intend to keep working in for a while, not for a quick look.

## Real-world scenario: an on-call engineer triaging three environments during an incident

An SRE gets paged for elevated error rates in `checkout`. The runbook says "confirm it's not also happening
in staging before you start rolling back prod" — a sequence that needs fast, low-error switching between
three clusters and namespaces under time pressure, which is exactly the failure mode these two tools exist
to remove:

```bash
kubectx prod-cluster && kubens checkout          # confirm the alert is real
kubectx staging-cluster && kubens checkout       # rule out a shared regression before touching prod
kubectx -                                        # back to prod-cluster in one keystroke, no retyping the name
kubectx -r prod-cluster                          # open a READ-ONLY scoped shell to poke around further, safely
```

Chaining `kubectx <ctx> && kubens <ns>` on one line is the standard idiom for "I need to be fully positioned
in one place before I run anything" — a single line an on-call engineer can paste from a runbook without two
separate steps that could be interrupted halfway.

## Real-world scenario: a CI/CD pipeline deploying to three environments from one kubeconfig

```yaml
# .github/workflows/deploy.yml
- name: Configure kubeconfig
  run: aws eks update-kubeconfig --name ${{ inputs.cluster }} --region us-east-1

- name: Point at the right context and namespace
  run: |
    kubectx $(kubectx | grep ${{ inputs.cluster }})
    kubens ${{ inputs.namespace }} --force

- name: Deploy
  run: kubectl apply -f k8s/
```

> [!WARNING]
> **Always pass `--force` to `kubens` in a fresh CI runner.** A brand-new runner's `kubeconfig` was just
> generated by `update-kubeconfig` seconds earlier — the API server might not yet be fully warmed up on a
> newly-scaled node pool, or the pipeline's service-account token might have a brief propagation delay. A
> non-forced `kubens` blocks on a live namespace-existence check that can flake in exactly that window;
> `--force` writes the value unconditionally and lets `kubectl apply` be the thing that actually surfaces a
> real connectivity failure, with a real error message, instead of `kubens` failing first with a more
> confusing one.

## Common pitfalls

- **Assuming `kubens` failures mean the namespace doesn't exist.** A bare `kubens <name>` failure is far more
  often a connectivity problem (VPN down, expired token, cold API server) than a genuinely missing
  namespace — check `kubens -c` (never hits the network) and `kubectl cluster-info` before concluding the
  namespace itself is the problem.
- **Forgetting a permanent `kubectx` switch is shell-session-wide, not command-wide** — see the CAUTION
  above. Reach for `-s`/`-r` for anything exploratory.
- **Scripting against `kubectx <name>` output parsing** instead of `kubectx -c` — the plain listing form can
  change shape if `fzf` integration is active in an interactive shell; `-c` is the stable, script-safe form
  for reading the current context.
- **Renaming a context and forgetting the old auto-generated name is what CI secrets/scripts elsewhere might
  still reference** — a rename is local to your kubeconfig only, but if a deploy script hardcodes the
  original long ARN-style name, renaming your local copy doesn't change what that script expects.

## Exit codes

`0` success · non-zero on a failed switch (unreachable API for `kubens`'s non-forced path, unknown context/
namespace name, or a malformed kubeconfig).

## When to reach for something else

For actually *browsing* what's running once you've switched — pods, logs, events, resource usage — reach for
k9s instead of chaining more `kubectl get` commands by hand; its own cheat sheet in this same category covers
launching it already scoped to a context/namespace via `--context`/`--namespace`, picking up right where
`kubectx`/`kubens` left off. For tailing logs across several pods matching a pattern once you're in the right
namespace, see the stern cheat sheet.
