Self-hosted service
doitintl/kube-no-trouble avatar
doitintl/kube-no-trouble

kube-no-trouble: checking a cluster for deprecated APIs before an upgrade finds them for you

Easily check your clusters for use of deprecated APIs

3,681 stars169 forksGoMIT

At a glance

What is it?
A small Go binary that reads your manifests the way you actually applied them, then tells you which API versions will stop working when you move the cluster forward. The interesting part is the collector design.
Who is it for?
Start with the file collector against your own manifest repository, because that is the one run that needs no cluster credentials and no elevated permissions. Whatever it reports is a real problem in a file you control.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A deprecation checker built around the manifests you actually applied

Kubernetes removes API versions on a schedule, and the removal does not announce itself in a way that stops your workloads. A Deployment created against `extensions/v1beta1` keeps running happily long after the endpoint behind it has been withdrawn, and the only moment you learn about it is the upgrade, when the API server no longer serves that path and your controller starts logging errors it cannot recover from. The failure mode is uncomfortable because it looks like the cluster broke, when in fact your stored manifest was always one version behind what the new control plane would accept.

`kubent` exists to move that discovery earlier. The repository description is one line, `Easily check your clusters for use of deprecated APIs`, and the README frames it in the same terms: check whether you are using any of these API versions, and upgrade your workloads before you upgrade your Kubernetes cluster. That ordering is the whole product. The tool does not perform upgrades, patch manifests or propose replacements. It produces a list of resources whose stored `apiVersion` is scheduled for removal, grouped by the Kubernetes release that retires them.

The project is MIT licensed and written in Go, with roughly 3,700 stars and 170 forks, and the default branch is `master`. Topics on the repository include `cluster`, `gke`, `hacktoberfest`, `k8s`, `kube` and `kubernetes`. The GKE topic is not incidental: managed Kubernetes offerings were where a large share of these deprecation cycles first became painful, because the provider schedules the control plane upgrade for you and gives you very little room to negotiate the date.

There is also a blog post linked from the README, by the maintainer, on Kubernetes deprecated APIs and the introduction of this tool. That link is worth reading if you want the reasoning behind the collectors, because the README itself explains what the tool inspects but not much about why it needed three different ways of looking.

Three collectors, because the original manifest lives in three different places

This is the design decision that makes `kubent` different from a schema validator run against a single directory of YAML. To tell you that a stored resource uses a deprecated API version, the tool has to find the manifest that created it, and where that manifest lives depends entirely on how the resource got there.

The README names three supported sources. The `file` collector reads local manifests in YAML or JSON, which is the obvious case and the one that needs no cluster at all. The `kubectl` collector reads the `kubectl.kubernetes.io/last-applied-configuration` annotation, which is the annotation `kubectl apply` writes so that it can compute a three-way merge on a later apply. If your resources went through `kubectl apply`, that annotation is a verbatim copy of what you sent, and it is the reason this collector works at all. The `Helm v3` collector reads Helm manifests stored as Secrets or ConfigMaps in individual namespaces, which is where Helm v3 puts release state by default.

The permission consequence is stated plainly in the README and it is the first thing to sort out before anything else works: you need sufficient permissions to read Secrets in the cluster to use the Helm collectors. On a hardened cluster that is often not a permission you have. The tool is not read-only in a way that avoids this, because Helm v3 stores its own release manifests as Secret objects and there is no non-Secret mirror to fall back on.

A resource created by some other tool, with neither the kubectl annotation nor Helm ownership, is invisible to the collectors. The README acknowledges this class of problem with a dedicated flag, `--additional-annotation`, for checking additional annotations for the last applied configuration, which is useful when a resource was applied with a tool other than kubectl. The flag can be given more than once. The honest summary is that `kubent` reports what it can find evidence of, and a cluster with unusual provisioning history will produce a clean report that means less than the same report on a conventionally managed cluster.

Installing it: a shell script, two package managers, and an explicit platform list

The README leads with a one-line installer, which is the conventional choice for a Go binary that publishes release archives:

bash
sh -c "$(curl -sSL https://git.io/install-kubent)"

The author anticipates the objection to piping a script from the internet into a shell and answers it in the next line: read what the script does first. The note explains that unless a version is specified the script downloads the latest version and unpacks it to `/usr/local/bin/`, which is the part worth confirming, since a tool that reads your cluster manifests is exactly the kind of binary you would rather place yourself.

There is a manual path for anyone who would rather not. The README links to the latest release page and says to download the archive for your CPU architecture and operating system and place the binary on your path. It then lists the platforms actually maintained: Linux amd64, Linux arm64, Darwin amd64, Darwin arm64 and Windows amd64. Five entries, and the gap is acknowledged rather than hidden, with a note that Windows on arm has historically not received the best support and may be added if that changes. An explicit list with a stated gap is more useful than a vague cross-platform claim.

Two third-party managers are documented, and the README attaches the same caveat to both: they are maintained by the community, and the packages may not always be up to date with the latest releases.

bash
brew install kubent
bash
scoop install kubent

Homebrew is linked to the upstream formula and Scoop to a third-party apps repository, which is a reasonable split between a package that a large project maintains itself and one that a smaller maintainer keeps in step with releases. For most people the choice comes down to platform rather than principle: the formula path is the macOS and Linux route, and the Scoop path is the Windows route.

The flags that matter are the ones that change what gets collected

Run against a cluster with no arguments and `kubent` reads the kubeconfig in the standard locations, uses whatever `current-context` is set, collects resources, and prints a report grouped by the Kubernetes version that retires each API. The default output is a text table with columns for kind, namespace, name and API version, which is the right default because the report is meant to be read by a person deciding what to fix first.

The help output is where the design shows through, because most flags exist to widen coverage rather than to configure presentation:

bash
./kubent -h
Usage of ./kubent:
  -a, --additional-kind strings         additional kinds of resources to report in Kind.version.group.com format
  -x, --context string                  kubeconfig context
  -e, --exit-error                      exit with non-zero code when issues are found
  -k, --kubeconfig string               path to the kubeconfig file
  -o, --output string                   output format - [text|json|csv] (default "text")
  -t, --target-version string           target K8s version in SemVer format (autodetected by default)

Two of these change behaviour rather than output. `-e`, or `--exit-error`, makes the tool exit non-zero when it finds issues, which is what turns a report into something you can gate a pipeline on. `-t`, or `--target-version`, overrides the autodetected cluster version, and the README explains exactly when you need that: in CI, using the file collector only, where there is no cluster from which to detect a version. The expected format is `major.minor[.patch]`, such as `1.16` or `1.16.3`.

Two more extend what counts as evidence. `-a`, or `--additional-kind`, takes custom resources in full `Kind.version.group.com` form, and the README's own example is `-a ManagedCertificate.v1.networking.gke.io`, which is a GKE resource and a fair hint about the tool's origin. `--additional-annotation` extends last-applied-configuration detection to other annotations, as described earlier. `-o` selects between `text`, `json` and `csv`, and `-l` sets the log level across the range from `trace` to `disabled`.

The `-e` flag is the one to reach for first. Once you have a clean run, putting `kubent` in CI with `-e` against your manifest repository gives you the same check every time someone merges, which is considerably earlier than the upgrade.

Inside the binary: OPA rulesets, Helm as a library, and a scratch container

The dependency list in `go.mod` explains the shape of the program better than the README does. Open Policy Agent appears at `v0.70.0`, which is the mechanism behind the rulesets you see named in the log output, files like `deprecated-1-16.rego` and `deprecated-1-20.rego`. A deprecation rule is a policy document evaluated against a resource, so adding support for a new Kubernetes release means adding a ruleset rather than writing new branching logic. The report grouping you see in the output corresponds to the loaded ruleset, which is why the log lines name versions explicitly.

`helm.sh/helm/v3` at `v3.13.3` is a direct dependency, not something reimplemented. The Helm v3 collector uses Helm's own library code to read release manifests out of Secrets and ConfigMaps, which is the only reliable way to interpret that storage format. `k8s.io/client-go` at `v0.28.4` carries an inline comment in the file that reads `Change me and break everything`, which is a fair summary of the risk in a tool whose entire job is talking to an API server. `k8s.io/apimachinery` sits two minor versions ahead at `v0.31.2`.

For structured output the project uses `github.com/ghodss/yaml` rather than the newer sigs.k8s.io fork, and `hashicorp/go-version` for version comparison, with `rs/zerolog` for the timestamped log lines and `spf13/pflag` for the flag parsing that produces the help text above.

The `Dockerfile` is worth reading as a small lesson in shipping a read-only diagnostic. It builds on `golang:1.23-alpine3.20` as a builder stage, copies the module files, runs `go mod download`, copies `cmd` and `pkg`, and builds through the Makefile with `CGO_ENABLED` set to zero by default in that Makefile. The runtime stage is `FROM scratch`, which means no shell, no package manager and no libc in the shipped image, `USER 10000:10000` for a non-root uid, and `ENTRYPOINT ["/app/kubent"]`. The published image is `ghcr.io/doitintl/kube-no-trouble:latest`, also tagged per release. A scratch image is the right shape for a tool that only makes API calls: there is nothing in it for an attacker to run.

Release cadence is nightly-only, and that shapes how you should pin it

The releases list has no version tags. Every entry is a nightly build: `nightly-0.7.3-43-g2366d92` from 2025-01-12, `nightly-0.7.3-40-g5f5c2c1` from 2024-12-22, `nightly-0.7.3-38-gfdc6df6` from 2024-11-20. The numbering reads as version 0.7.3 plus a commit offset plus a short hash, which is what an automated build produces when nobody is cutting numbered releases by hand.

That has a practical consequence for anyone wiring this into CI. There is no stable channel to pin to, so pinning means pinning a nightly tag or a commit, and a nightly tag is a moving target by construction. If you gate a merge on `kubent`, decide deliberately whether you want a moving ruleset (new deprecations appear as soon as they are committed) or a frozen one (pin the tag), because that choice determines whether a merge can start failing because of a policy change rather than a manifest change.

The release bodies are generated rather than written. Each one carries a Docker image reference, a changelog with Features, Fixes and Internal sections, commit links with author attribution, and a new-contributors list. That output shape comes from `cliff.toml` at the repository root, the conventional-commits changelog generator. Worth noticing in the entries: a feature adding rego for v1.32 deprecations, a fix for the install script in a dumb TERM, and a fix that adds the Docker image back, which tells you the image has been absent before.

The tree is tidy for a project this size: `cmd/` and `pkg/` for the code, `docs/` for the images and documentation, `fixtures/` for test data, `test/` for the test suite, `scripts/` for build helpers including the alpine setup the Dockerfile calls. Root files include `Makefile`, `Dockerfile`, `go.mod`, `go.sum`, `cliff.toml`, `.codespellrc`, `.mise.toml` and `.pre-commit-config.yaml`. Open issues number around 30. The last push was on 2026-09-17, so the tool that tells you what will break on upgrade is itself being maintained.

Editorial conclusion

Start with the file collector against your own manifest repository, because that is the one run that needs no cluster credentials and no elevated permissions. Whatever it reports is a real problem in a file you control. Once that is clean, point it at a cluster with `kubent` and no flags and read the collectors line by line, because the difference between the two runs tells you which of your manifests the cluster knows about and you do not. The ruleset is Rego loaded at runtime, so a new Kubernetes deprecation lands as a new ruleset rather than a new binary feature, and that is the design decision worth understanding before you decide whether to depend on it. The documentation settles the collectors, the flags and the supported platforms. It does not settle how to read an empty Helm line, and that is worth working out on a cluster you control. The last push was on 2026-09-17.

Frequently asked questions

What does kube-no-trouble actually check in my cluster?

It looks for stored manifests that use Kubernetes API versions scheduled for removal, using one of three collectors: local YAML or JSON files, the kubectl last-applied-configuration annotation, or Helm v3 release manifests stored as Secrets and ConfigMaps. It reports resources grouped by the release that retires them; it does not change anything.

Why does kubent need permission to read Secrets?

Helm v3 stores each release's manifests as Secret objects in the namespace it installs into, so the Helm collector has to read Secrets to find them. On a cluster where reading Secrets is restricted, expect the Helm collector to return nothing and rely on the file and kubectl collectors instead.

Can I run kube-no-trouble in CI without access to a cluster?

Yes. Use the file collector against your manifest repository and pass the target version explicitly with -t, because the tool cannot autodetect a cluster version when there is no cluster to query. Add -e so the command exits non-zero when it finds deprecated APIs and the pipeline fails.

How do I pin a version of kube-no-trouble?

Published releases are nightly tags built from the commit history, such as nightly-0.7.3-43-g2366d92, rather than numbered stable versions. Pin the specific nightly tag or a commit hash if you want a fixed ruleset, and expect a moving tag to pick up new deprecation rules as they are committed.

Official sources

  1. doitintl/kube-no-trouble on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/doitintl-kube-no-trouble.svg)](https://hysenlabs.com/projects/doitintl-kube-no-trouble)