Skip to content
Kane, Ethan CLI plugin Rev 2026.09

004

kubectl-fleet

A kubectl plugin for multi-cluster operations. Fans queries out across every kubeconfig context in parallel and merges the results into one context-aware table: incident triage, rollout parity checks, and fleet-wide audits without juggling terminal tabs.

Origin
Open Source
Stack
Goclient-goCobraGoReleaser
Release
v0.1.1
Licence
Apache-2.0
Recording 96×30 · 0:17
# Three kind clusters, three real kubeconfig contexts.
# Nothing here is merged ahead of time.

$ ./bin/kubectl-fleet contexts --filter '^kind-fleet-demo-' --check
CONTEXT             CLUSTER             NAMESPACE   AUTH                REACHABLE   VERSION   LATENCY      ERROR
kind-fleet-demo-a   kind-fleet-demo-a               kind-fleet-demo-a   yes         v1.36.1   4.106625ms
kind-fleet-demo-b   kind-fleet-demo-b               kind-fleet-demo-b   yes         v1.36.1   4.956541ms
kind-fleet-demo-c   kind-fleet-demo-c               kind-fleet-demo-c   yes         v1.36.1   4.401833ms

# Reachability is a live probe of each API server, not a cached list.

$ ./bin/kubectl-fleet status --contexts '^kind-fleet-demo-' --since 10m
CONTEXT             VERSION   NODES   PODS   PENDING   CRASHLOOP   RESTARTS_10m0s
kind-fleet-demo-a   v1.36.1   1/1     11     0         0           0
kind-fleet-demo-b   v1.36.1   1/1     12     0         1           1
kind-fleet-demo-c   v1.36.1   1/1     12     0         0           0

# One cluster is unhealthy. The fleet view is how you noticed.

$ ./bin/kubectl-fleet get deploy -n payments --contexts '^kind-fleet-demo-'
CONTEXT             NAMESPACE   NAME     READY   UP-TO-DATE   AVAILABLE   AGE
kind-fleet-demo-a   payments    api      2/2     2            2           5m
kind-fleet-demo-b   payments    api      2/2     2            2           5m
kind-fleet-demo-b   payments    crashy   0/1     1            0           5m
kind-fleet-demo-c   payments    api      3/3     3            3           5m

# One table, three API servers, queried in parallel.
Three local kind clusters, three real kubeconfig contexts. The merged table was assembled from three live API servers during the take.

The gap this fills

kubectl is built around one cluster at a time. That assumption is invisible until the estate has a hundred and fifty of them, and then it is the whole problem.

The question during an incident is almost never “what is happening in this cluster”. It is “which clusters are affected”, “is this version everywhere”, or “did that rollout land in all three regions”. Answering any of those with stock tooling means a for loop over contexts, output that arrives interleaved, and no column telling you which cluster a row came from. Most people end up with a terminal tab per cluster and their own eyes as the join.

$ kubectl fleet status --contexts '^prod-'
CONTEXT      VERSION      NODES   PODS   PENDING   CRASHLOOP
prod-eu-1    v1.31.2      6/6     142    0         0
prod-us-1    v1.31.2      6/6     138    2         1
prod-ap-1    v1.30.6      4/4     97     0         0

One command, one table, and the version drift on prod-ap-1 is visible without reading anything carefully.

How it works

Contexts are selected by regex against the kubeconfig, queried in parallel with client-go, and merged into one table with a leading CONTEXT column so identically named resources across clusters line up.

The plugin plumbs genericclioptions.ConfigFlags, so the standard kubeconfig flags behave exactly as they do in kubectl: --kubeconfig, --context, --namespace, --user, --cluster, --token, --server. KUBECONFIG is honoured including colon-separated multi-file merges. Nothing about the mental model changes, which is most of why it gets used.

Three commands do the work:

  • kubectl fleet contexts lists contexts, with an optional parallel probe of each one’s /version endpoint reporting reachability and latency.
  • kubectl fleet get is a parallel kubectl get across the selected contexts.
  • kubectl fleet status is a per-cluster health snapshot: node readiness, pod counts, server version, and the three noisiest namespaces by non-Running pods.

Everything honours -o table, wide, json and yaml, so a fleet-wide question can be piped into jq:

kubectl fleet status -o json | jq '.[] | select(.PodsCrashLoop > 0)'

The decision that matters most

A failed cluster never aborts the run. Per-context errors land in the table with <error> in the name column and a warn: line on stderr; every other cluster still answers.

This sounds like an implementation detail and it is the difference between a tool that is usable during an incident and one that is not. Something is always unreachable across a fleet that size. A tool that exits non-zero on the first timeout is a tool that stops working precisely when a cluster is down, which is the only time anyone reaches for it.

Distribution

Released through GoReleaser: multi-arch archives for linux, darwin and windows across amd64 and arm64, with checksums attached to the GitHub release and notes grouped from conventional commit messages. go install and a build from source both work.

Krew is deliberately deferred until the plugin is feature-complete. Submitting to krew-index early means committing to an interface before it has been used enough to know whether it is the right one.