Free & open source · Apache-2.0

Find what breaks before you upgrade Kubernetes.

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 →

kube-upgrade-check --target 1.34
kube-upgrade-check scanning a live cluster and printing what an upgrade to 1.34 would break

A real scan, not a mock-up.

Why this finds more than a manifest scan

The API server converts objects on read. Your manifests are not the whole story.

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.

What it checks

Every rule is data, in the repo, with the upstream sentence it was transcribed from.

FamilyWhat it findsRules
Removed APIsObjects still written at an API version the target removes, with the manager and date that wrote them97
Config breakersControl-plane flags and kubelet settings the target refuses to start with, including feature gates locked to their default382
Volume pluginsIn-tree volume plugins that stop mounting17
Node runtimeContainer runtime, cgroup version, kubelet and kube-proxy version skew8
Add-on compatibilityWhether Karpenter, ingress-nginx, cert-manager, Argo CD, CoreDNS, Istio and kube-proxy support the target7 catalogs
AdvisoriesBehaviour changes on the path that no API call can settle34
The part we care most about

It tells you what it could not check.

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.

COULD NOT SEE (1)
- control-plane flags — 310 rules not checked
no static control-plane pods were found. This is normal on a managed control plane (EKS, GKE, AKS)…
check: kubectl get pods -n kube-system -l tier=control-plane

How this differs from Pluto and kubent

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.

Plutokubentkube-upgrade-check
Removed and deprecated APIsyesyesyes
Scans manifests and Helm charts in a repoyesyesno
Reads stored Helm release manifestsyesyesno
Managed-fields evidence (names the writer)nonoyes
Control-plane and kubelet settings the target rejectsnono382 rules
In-tree volume plugins that stop mountingnono17 rules
Node runtime and version skewnonoyes
Add-on compatibility (Karpenter, ingress-nginx…)nono7 catalogs
Reports what it could not checknonoyes

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.

Gate a pipeline on it

$ kube-upgrade-check --target 1.34 --fail-on high
$ kube-upgrade-check --strict
$ kube-upgrade-check -o json | jq .

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.

Read-only, by construction

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.

Scan a cluster in two minutes.

No account, no agent, no signup. Apache-2.0, and the whole rule catalog is JSON in the repo.

Read the source →
Who builds this

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 →