One kubectl workflow across every cluster.
kx is a small, Linux-first operator shell for people who already know
kubectl but work across more than one cluster. Select contexts as a fleet,
compare their state, investigate incidents, and guard risky fan-out without
changing your current context.
It does not replace kubectl or introduce another Kubernetes API client.
Ordinary commands pass through unchanged; fleet behavior starts only when you
add an explicit @selector.
kx get pods -A
kx @prod matrix -n payments
kx @prod matrix deploy/api -n payments --cols context,ready,image,rollout
kx @prod why deploy/api -n payments --deep
kx @prod logs deploy/api -n payments --since 15m --grep 'error|panic|timeout'
kx @prod diff deploy/api -n payments# Verify the local setup and see your contexts.
kx doctor
kx ctx ls
# Add local metadata (kx never edits kubeconfig for this).
kx ctx tag aks-prod-weu env=prod region=eu team=payments risk=high
# Preview exactly what @prod means, then run ordinary kubectl safely.
kx ctx where @prod
kx @prod --dry-run get pods -A
kx @prod get pods -A
# Compare one workload and investigate the unhealthy copy.
kx @prod matrix deploy/api -n payments
kx @prod why deploy/api -n paymentsTry kx help matrix, kx help selectors, or any command with --help.
kubectx is excellent for switching one active context. k9s and dashboards
are excellent for exploring one cluster interactively. kx targets a
different gap: repeatable, terminal-native questions and actions across a set
of contexts, with the selected set visible before a mutation runs.
Good fits include platform teams operating regional clusters, staged
rollouts, incident comparison, configuration drift checks, and fleet-wide RBAC
audits. If you use only one cluster, plain kubectl is usually enough.
kx treats kubeconfig contexts like a queryable fleet:
- selectors:
@prod,@prod.eu,@env=prod,@team:payments,@/regex/ - context tags that never touch kubeconfig
- guarded fan-out across many contexts
- incident commands:
why,events,logs - fleet views:
matrix,diff,drift - RBAC checks:
can,access - context hygiene:
ctx scan,ctx clean - shell glue:
completion,shell-init,prompt - Krew packaging template for release distribution
The mental model is:
selector + command + policy + output
Latest Linux amd64 release:
tmp="$(mktemp -d)"
curl -fsSL -o "$tmp/kx.tar.gz" \
https://github.com/GabboPenna/kx/releases/latest/download/kx-linux-amd64.tar.gz
tar -xzf "$tmp/kx.tar.gz" -C "$tmp"
sudo install -m 0755 "$tmp/kx-linux-amd64/kx" /usr/local/bin/kx
kx --versionPinned release with checksum verification:
version="v0.3.0"
base="https://github.com/GabboPenna/kx/releases/download/$version"
curl -fsSLO "$base/kx-linux-amd64.tar.gz"
curl -fsSLO "$base/checksums.txt"
sha256sum -c checksums.txt --ignore-missing
tmp="$(mktemp -d)"
tar -xzf kx-linux-amd64.tar.gz -C "$tmp"
sudo install -m 0755 "$tmp/kx-linux-amd64/kx" /usr/local/bin/kxFrom source:
go install github.com/GabboPenna/kx@latestLocal hacker loop:
git clone https://github.com/GabboPenna/kx
cd kx
make test
make build
sudo install -m 0755 bin/kx /usr/local/bin/kxSelectors start with @.
| Selector | Meaning |
|---|---|
@all |
every kubeconfig context |
@current |
current kubectl context |
@prod |
fuzzy match context, cluster, user, namespace, and tags |
@prod.eu |
match all facets |
@env=prod |
match local kx tag |
@team:payments |
same as @team=payments |
@/aks-prod-.*/ |
regex against context names |
@prod,@staging |
union of selectors |
Tag contexts once, then stop memorizing cloud-provider soup:
kx ctx tag aks-prod-weu env=prod region=eu team=payments risk=high
kx ctx tag aks-stage-weu env=staging region=eu team=payments
kx ctx where @prod.euNormal kubectl still works:
kx get pods -A
kx apply -f app.yamlFan out when you add a selector:
kx @prod --parallel 8 get deploy -n payments
kx @prod --timeout 20s get nodes -o wide
kx @prod --canary 1 rollout restart deploy/api -n paymentskx never mutates the global current context while doing selector fan-out.
why is the "tell me what hurts" command. It gathers the resource JSON,
kind-aware facts, conditions, owner chain, rollout status, warning events, and
optional deep describe/log detail.
kx why pod/api-6b7df8 -n payments
kx why svc/api -n payments
kx @prod why deploy/api -n payments
kx @prod why deploy/api -n payments --deepEvents without the archaeological dig:
kx events -n payments --warnings --since 30m
kx @prod events -A --warnings --limit 80Logs get context prefixes, grep, and all-container defaults:
kx logs deploy/api -n payments --since 10m
kx @prod logs deploy/api -n payments --since 15m --grep 'error|panic|timeout'The matrix is the little ops table you wanted every time a namespace started acting cursed at 02:00.
Resource mode compares one target across contexts:
kx @prod matrix svc -n payments
kx @prod matrix deploy/api -n payments
kx @prod matrix deploy/api -n payments --cols context,ready,image,replicas,rolloutMatrix output is kind-aware by default:
svc -> TYPE, CLUSTER-IP, EXTERNAL-IP, PORTS, ENDPOINTS, SELECTOR
pod -> STATUS, READY, RESTARTS, POD-IP, NODE, IMAGE
deploy -> STATUS, READY, REPLICAS, IMAGE, ROLLOUT
ingress -> CLASS, HOSTS, ADDRESS, PORTS, STATUS
job -> STATUS, COMPLETIONS, SUCCEEDED, FAILED
cronjob -> STATUS, SCHEDULE, SUSPEND, ACTIVE, LAST-SCHEDULE
pvc/pv -> STATUS, VOLUME, CAPACITY, ACCESS-MODES, STORAGECLASS
node -> STATUS, ROLES, INTERNAL-IP, VERSION
Namespace mode scans a whole namespace and prints a compact inventory using
generic kind-aware columns: STATUS, DETAIL, NETWORK, ENDPOINTS, and
AGE.
kx matrix -n payments
kx @prod matrix -n payments
kx @prod matrix -n payments --resources deployments,pods,services,ingresses
kx @prod matrix -n payments --cols context,kind,name,status,ready,ageBy default namespace mode checks:
deployments,statefulsets,daemonsets,pods,services,ingresses,jobs,cronjobs
Output is intentionally boring in the best possible way: fixed table headers,
bounded columns, deterministic sorting, and enough status translation to spot
ImagePullBackOff, degraded workloads, suspended CronJobs, pending Ingresses,
pending LoadBalancers, and Services with zero ready endpoints without cracking
open another pane.
Sanitized resource diff. It strips runtime noise like status, uid,
resourceVersion, managedFields, and timestamps before comparing.
kx @staging,@prod diff deploy/api -n paymentsWhen you only need the shape of the problem:
kx @prod drift deploy/api -n paymentsCheck one permission across the fleet:
kx @prod can delete pods -n kube-system
kx @prod can create deployments -n paymentsPrint a quick access matrix:
kx access -n payments
kx @prod access -n payments --resources pods,secrets,deployments --verbs get,list,create,deleteFind kubeconfig fragments:
kx ctx scan ~/.kube ~/.kube/configs.dClean local kx metadata for contexts that no longer exist in your active
kubeconfig:
kx ctx clean --dry-run
kx ctx cleankx stores its own metadata under:
~/.config/kx
Override it when you want disposable labs:
export KX_HOME="$PWD/.kx"Completion:
kx completion bash > ~/.local/share/bash-completion/completions/kx
kx completion zsh > ~/.zfunc/_kx
kx completion fish > ~/.config/fish/completions/kx.fishPrompt segment:
kx promptFull shell init:
eval "$(kx shell-init bash)"That gives you command completion plus a tiny context segment in your prompt. The point is constant context awareness without a spaceship prompt framework.
Mutating commands such as apply, delete, patch, run, expose, scale,
rollout restart, drain, cordon, label, and annotate trigger
confirmation when they target multiple contexts or prod-like contexts. The
guard also recognizes kubectl global flags placed before the command.
A context is prod-like when:
- its name contains
prod - the selector contains
prod - it has
env=prod - it has
risk=high
Automation can opt in explicitly:
kx @prod --yes rollout restart deploy/api -n payments
KX_FORCE=1 kx @prod rollout restart deploy/api -n paymentsRelease assets are shaped for Krew. Generate a manifest after a release:
version="v0.2.0"
curl -fsSLO "https://github.com/GabboPenna/kx/releases/download/$version/checksums.txt"
scripts/render-krew-manifest.sh "$version" checksums.txt > packaging/krew/kx.yamlThe template lives at:
packaging/krew/kx.template.yaml
kx uses SemVer tags:
v0.1.0
v0.2.0
v1.0.0
Pushing a tag creates a GitHub Release with:
kx-linux-amd64.tar.gz
kx-linux-arm64.tar.gz
checksums.txt
kx-krew.yaml
The binary embeds tag, commit, and build timestamp:
kx --versionSee docs/RELEASING.md for the release flow.
kx is written in Go because this job wants:
- one boring binary
- fast startup
- cheap fan-out concurrency
- Linux-first release assets
- easy process control around
kubectl - no dependency carnival during an incident
The first rule is compatibility: unknown Kubernetes behavior goes to kubectl.
The second rule is context sanity: fleet operations should be explicit,
queryable, repeatable, and hard to fat-finger.
MIT
