Point it at a cluster and it tells you what an upgrade to a given version would break: removed APIs still in use, control-plane and kubelet settings the new version refuses to start with, in-tree volume plugins that stop mounting, version skew, and whether the add-ons you run support the version you are moving to.
It reads your cluster and writes nothing. No account, no agent, no data leaves the machine.
brew install runtimez-com/tap/kube-upgrade-check
Also on Linux via curl | sh, on Windows via Scoop, and as .deb / .rpm / .apk packages.
macOS, Linux and Windows on amd64 and arm64.
All install options →
A real scan, not a mock-up.
Ask for a Deployment at apps/v1 and you get apps/v1,
whatever wrote it. So the usual answer is to read the manifest a client stored: a Helm release
secret, or the last-applied-configuration annotation.
That works, and it misses everything applied another way. Server-side apply writes no annotation.
Nor does kubectl create, kubectl edit,
an operator, Flux, or Argo CD.
metadata.managedFields says which client last wrote each part of an
object, at which API version, and when. An entry at a removed
version means those fields have not been rewritten since.
So a finding here can say helm wrote this at flowcontrol.apiserver.k8s.io/v1beta3 on 2024-03-02 — the writer and the date, per object, with no manifest stored anywhere.
Every rule is data, in the repo, with the upstream sentence it was transcribed from.
| Family | What it finds | Rules |
|---|---|---|
| Removed APIs | Objects still written at an API version the target removes, with the manager and date that wrote them | 97 |
| Config breakers | Control-plane flags and kubelet settings the target refuses to start with, including feature gates locked to their default | 382 |
| Volume plugins | In-tree volume plugins that stop mounting | 17 |
| Node runtime | Container runtime, cgroup version, kubelet and kube-proxy version skew | 8 |
| Add-on compatibility | Whether Karpenter, ingress-nginx, cert-manager, Argo CD, CoreDNS, Istio and kube-proxy support the target | 7 catalogs |
| Advisories | Behaviour changes on the path that no API call can settle | 34 |
Every check that could not run is printed, with the reason and a command to run yourself. On EKS, GKE and AKS the provider runs the control plane out of reach, so a few hundred config rules genuinely cannot be evaluated — and the report says so with a count, rather than quietly returning a shorter list of findings.
This matters more than it sounds. The failure mode of an upgrade checker is not a wrong finding, which
someone will notice. It is a clean report that was clean because half the checks never ran.
--strict turns any gap into a non-zero exit for pipelines that would
rather fail than proceed on partial information.
Both are good tools and this is not a replacement for them. They answer one question — am I using an API version that is going away? — and answer it well. If that is your question, either will serve you.
| Pluto | kubent | kube-upgrade-check | |
|---|---|---|---|
| Removed and deprecated APIs | yes | yes | yes |
| Scans manifests and Helm charts in a repo | yes | yes | no |
| Reads stored Helm release manifests | yes | yes | no |
| Managed-fields evidence (names the writer) | no | no | yes |
| Control-plane and kubelet settings the target rejects | no | no | 382 rules |
| In-tree volume plugins that stop mounting | no | no | 17 rules |
| Node runtime and version skew | no | no | yes |
| Add-on compatibility (Karpenter, ingress-nginx…) | no | no | 7 catalogs |
| Reports what it could not check | no | no | yes |
Where they are still the better choice: checking a change before it is applied. Pluto and kubent both scan static manifests and Helm charts in a repository. This tool reads a live cluster, so it cannot review a pull request. If you manage everything with Helm, run one of them as well — the two questions have different answers, and only one of them is in git.
Exit codes are a contract: 0 clean, 2 bad flags, 3 credentials refused, 4 findings crossed your
threshold or something could not be checked. An unknown --fail-on
value is rejected rather than ignored, so a typo cannot quietly turn the gate into a no-op.
Your own kubeconfig is enough. For CI, the repo ships a ClusterRole with nothing but
get and list.
The two permissions people ask about: nodes/proxy reads each
kubelet's live configuration, and /metrics reads the API server's
count of deprecated requests. Without either, the report says which checks it lost rather than
skipping them silently.
No account, no agent, no signup. Apache-2.0, and the whole rule catalog is JSON in the repo.
We build Runtimez, a commercial deployment-intelligence platform for Kubernetes, and this CLI is the same evaluators running from your laptop against one cluster. It is free, it will stay free, and it is genuinely useful on its own — that is the point of shipping it.
What the CLI does not do is watch. If you want the same checks running continuously across a fleet, with history, and correlated against image CVEs and cost, that is the paid product. If you only ever run the CLI, that is a completely fine outcome.
See what the platform adds →