Install
# Debian / Ubuntu (via mise / cargo / direct binary — the apt package lags)
K9S_VER=0.40.10
curl -L -o /tmp/k9s.tgz \
"https://github.com/derailed/k9s/releases/download/v${K9S_VER}/k9s_Linux_amd64.tar.gz"
tar -xzf /tmp/k9s.tgz -C /tmp k9s
sudo install /tmp/k9s /usr/local/bin
# macOS
brew install derailed/k9s/k9s
# Arch
sudo pacman -S k9s
# Or via mise
mise use -g k9s@latest
Verify:
k9s version
The model
k9s uses the current kubeconfig ($KUBECONFIG or ~/.kube/config). Launch with k9s — the home view shows pods in the current namespace. The bottom of the screen lists relevant keybindings; ? opens the full help.
The command bar (top, prefixed with :) lets you jump to any resource type:
:pods list pods (also Ctrl-A in many contexts)
:deployments
:svc services
:cm configmaps
:secrets
:ing ingresses
:no nodes
:ns namespaces
:pv persistent volumes
:pvc persistent volume claims
:job
:cronjob
:crd custom resource definitions
:events cluster events stream
:contexts switch kubeconfig context
For any resource type that exists in the cluster — including CRDs — the same keybindings apply.
The keystrokes worth knowing
# Navigation
↑/↓ move cursor
PgUp/PgDn page through long lists
/ filter the current list (regex)
Esc clear filter / go back
0 show all namespaces
1..9 jump to namespace 1..9
# On the selected resource
enter drill in (e.g. pod → containers; deployment → pods)
y view YAML
e edit YAML in $EDITOR (kubectl edit semantics)
d describe (kubectl describe)
l logs (then s for follow / wrap / timestamps options)
s shell into the container
f port-forward (prompts for local port)
x scale (deployments / statefulsets / replicasets)
Ctrl-d delete (with confirmation)
Ctrl-k kill (force-delete pod)
Ctrl-r refresh
# Cluster-wide
:pulses overview dashboard with CPU/memory/pods/nodes graphs
:xray pod "X-ray" view: tree of pod → container → mounted volumes/configmaps
:popeye run Popeye (cluster sanitizer; flags misconfigurations)
Filtering and search
/ filters the current view by regex on the displayed columns. Two useful patterns:
# Filter pods to those in "Failed" phase
/Failed
# Filter by label
/!app=ignore-me # negate
/-l app=web # explicit label selector
# Filter across all namespaces
0 # toggle "show all namespaces"
/payments # then filter to anything containing "payments"
Aliases that pay off
k9s ships with built-in aliases; custom ones go in ~/.config/k9s/aliases.yaml:
aliases:
vs: v1/services
vd: v1/secrets
hr: helm.cattle.io/v1/helmreleases
app: argoproj.io/v1alpha1/applications
Then :app in the command bar shows all Argo CD Applications.
Plugins
~/.config/k9s/plugins.yaml defines per-context-menu actions. Example: a "tail the log to /tmp" plugin:
plugins:
tail-to-file:
shortCut: Shift-T
description: "Tail log to /tmp"
scopes: [pods, containers]
command: bash
background: false
args:
- -c
- "kubectl logs --tail=-1 -f -n $NAMESPACE $POD -c $CONTAINER > /tmp/${POD}.log"
Now Shift-T on any pod tails to a file. Plugins can call any shell command and access $POD, $NAMESPACE, $CONTAINER, $CONTEXT, $CLUSTER, etc.
Configuration
~/.config/k9s/config.yaml:
k9s:
liveViewAutoRefresh: true
refreshRate: 2
maxConnRetry: 5
enableMouse: true
headless: false
logoless: false
crumbsless: false
readOnly: false
noExitOnCtrlC: false
ui:
enableMouse: true
headless: false
logoless: false
skin: dracula # theme; pick from ~/.config/k9s/skins/
thresholds:
cpu: { critical: 90, warn: 70 }
memory: { critical: 90, warn: 70 }
shellPod:
image: busybox:latest
namespace: default
limits: { cpu: 100m, memory: 100Mi }
The "shell pod" is the throwaway pod k9s creates when you press :shell to debug network connectivity from inside the cluster. Customize the image (a debian + curl + dig + netcat image is a much more useful base than plain busybox).
Read-only mode for safer demos
k9s --readonly
Disables edit / delete / scale operations. Useful for production demos or when sharing a screen.
Multi-cluster workflow
# Switch context from inside k9s
:ctx # context picker
# Or change kubeconfig per-launch
KUBECONFIG=~/.kube/prod-config k9s
KUBECONFIG=~/.kube/dev-config k9s
The status bar shows the active context and namespace; it's hard to delete the wrong production resource by accident when "PROD" is in red at the top.
Pulses and Xray: the views worth exploring
:pulses— cluster overview: nodes, pods, CPU / memory / network / disk graphs, alerts. Useful "is anything obviously wrong" view.:xray pod— per-pod tree showing containers + every ConfigMap/Secret/PVC mounted into them. Catches "is this pod actually getting the latest config" questions in one screen.:popeye— cluster sanitizer pass. Flags resource-limit-less containers, missing readiness probes, services with no endpoints, deprecated APIs, etc. Worth running periodically.
Where k9s loses to kubectl
- Anything scripted — pipelines, CI, automation.
kubectlis the right tool there. - Cross-cluster operations —
kubectl --context A get podsover many contexts is easier than navigating each in k9s. - Detailed history / audit — for an audit trail of what you did,
kubectl+ shell history beats k9s, which leaves no trail of its key presses.
For everyday "look at the cluster, fix one thing, move on," k9s is the fastest workflow available. Pair with Argo CD (see that tutorial) for the longer-term declarative state and k9s for the immediate interactive operations.