controller-tools pairs a Go version with a client-go version, and that is the rule
Tools to use with the controller-runtime libraries
At a glance
- What is it?
- The Kubernetes controller-tools project is a set of Go libraries for building controllers, and its most consequential decision is documented as a version policy rather than a feature. Each minor release of controller-tools is built and tested against one specific minor version of client-go, and the minimum Go version follows from the dependencies, so upgrading is a coordinated act rather than a library bump.
- Who is it for?
- controller-tools is the right dependency for a team writing Kubernetes controllers in Go with the controller-runtime stack, and it is the sensible choice over hand-rolled code generation because its output follows the upstream conventions. Skip it if you only need to read manifests, and expect a version decision whenever the client-go version moves, since the compatibility pairing is deliberate.
- Can I use it commercially?
- Yes. Apache-2.0 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 8 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 October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A thin README over a heavily specified project
The controller-tools README is short, and the reason is that this is not a project that sells features. It opens by calling itself a set of Go libraries for building Controllers, and then spends most of its length on compatibility, process and community.
The licence is Apache-2.0, the primary language is Go, and the module path in `go.mod` is the sigs.k8s.io path that the rest of the Kubernetes ecosystem uses.
module sigs.k8s.io/controller-toolsThe repository layout tells you what is inside. There is a `cmd/` directory, which is where command line entry points live, a `pkg/` directory for the library packages, a `hack/` directory holding the development scripts, a `Makefile`, and a `test.sh`. There are also `VERSIONING.md` and `RELEASE.md` for the release process, `CONTRIBUTING.md`, `OWNERS` and `OWNERS_ALIASES` for the ownership model, `SECURITY_CONTACTS`, and a `code-of-conduct.md` that points back to the Kubernetes community code of conduct.
Two scripts at the top of the tree are worth noting because their names describe what the project does day to day. `.run-controller-gen.sh` runs the code generator, and `.run-in.sh` runs the project inside a container, which is the convenient way to work on a tool that needs a specific Go version and a set of pinned tools.
The README also states where the project sits. It is a subproject of the kubebuilder project in sigs apimachinery, which is the standard scaffolding tool for Kubernetes operators, and the maintainers are reachable on the kubebuilder Slack channel and a Google Group.
One controller-tools minor per client-go minor
The compatibility section is the part of the README that anyone integrating this into a build has to read, and its message is unambiguous.
The README says that every minor version of controller-tools has been tested with a specific minor version of client-go, that a controller-tools minor version may be compatible with other client-go minor versions but that this is by chance and neither supported nor tested, and that in general one minor version of controller-tools is created for each minor version of client-go and the other k8s.io dependencies.
The table gives the current pairs.
| CT v0.21 | v0.36 | 1.26 |
| CT v0.20 | v0.35 | 1.25 |
| CT v0.19 | v0.34 | 1.24 |So the three columns are the controller-tools release, the k8s.io and client-go release it targets, and the minimum Go version that release needs.
The reason to spell this out rather than leave it to a semver range is that controller-tools reads and writes the Kubernetes API types. The dependency manifest makes the coupling concrete, with `k8s.io/api`, `k8s.io/apimachinery`, `k8s.io/apiextensions-apiserver`, `k8s.io/apiserver` and `k8s.io/code-generator` all pinned at v0.37.0 in the current tree, alongside `k8s.io/gengo/v2` and `k8s.io/kube-openapi`.
The practical consequence is that controller-tools is not a library you bump opportunistically. When the client-go version your controller uses moves, the generator that produces your manifests and code moves with it, and the two have to land in the same dependency update. The `envtest-releases.yaml` file at the top of the repository shows the same idea applied to test binaries, with a separate release line for envtest, which is the v1.37.0 tag in the recent release list.
The Go floor is inherited, not chosen
The README explains the minimum Go version as a derived fact rather than a policy, and the explanation is short enough to quote.
The minimum Go version of controller-tools is the highest minimum Go version of the Go dependencies, and usually this is identical to the minimum Go version of the corresponding k8s.io dependencies. The current `go.mod` declares it directly.
go 1.26.0So the floor moves when the Kubernetes libraries move, and the table of versions and Go requirements in the README is really a table of upgrade coordinates.
The Makefile encodes the same number for the build itself.
GO_VERSION ?= 1.26.0
GOTOOLCHAIN = go$(GO_VERSION)The toolchain variable is exported, which means the build asks the Go toolchain manager for that specific version rather than using whatever is on the path. The rest of the Makefile is the usual careful setup for a Go project that has to work on more than one machine: the shell is fixed to bash, module mode is switched on explicitly, and the proxy is taken from the environment with a fallback to the public Go module proxy when it is not set.
There is one non-Go detail in there that says something about the project's audience. The Makefile detects whether SELinux is enforcing and, if so, adds the `:z` option to volume mounts, which is the flag that tells the container runtime to relabel mounted paths for SELinux. It is a small line, and it is the kind of line that only exists in a project whose CI and contributors run on Red Hat family systems.
A command line in front of a set of libraries
The dependency list separates what controller-tools is from what it is built with.
The libraries it provides live in `pkg/`, and the entry points that expose them live in `cmd/`. The command line itself is built with `spf13/cobra` at version 1.10.2 and `spf13/pflag` for flag handling, and the terminal output uses `fatih/color` and `gobuffalo/flect`.
github.com/spf13/cobra v1.10.2
github.com/gobuffalo/flect v1.0.3
github.com/fatih/color v1.19.0Flect is worth a note because it explains something you would otherwise hit as a surprise. Flect is an inflection library, used here to turn a kind name into a plural, which is exactly the kind of operation a Kubernetes code generator needs when it produces one type per object and a list type beside it.
The analysis machinery underneath comes from `golang.org/x/tools`, which is the package loading and type checking infrastructure that any tool working on Go source needs in order to understand a package rather than merely read it as text. `go.yaml.in/yaml/v3` and `sigs.k8s.io/yaml` handle the YAML, and `k8s.io/gengo/v2` is the Kubernetes code generation library the project builds on.
The test dependencies describe how the project is verified. `onsi/ginkgo/v2` and `onsi/gomega` are the BDD style test frameworks Kubernetes projects tend to use, `google/go-cmp` is there for comparing generated output, and `golang.org/x/tools/go/packages/packagestest` provides the fixtures for testing package loading behaviour.
The split between `cmd/` and `pkg/` is the ordinary Go arrangement, and it is the reason other projects can depend on controller-tools as a library rather than only shelling out to the binary. A project that wants the same code generation inside its own build can import the packages, and the same split is what lets kubebuilder depend on it.
envtest and the matrix of test binaries
The `hack/` directory holds the test infrastructure, and the Makefile names it explicitly.
ENVTEST_DIR := hack/envtest
ENVTEST_MATRIX_DIR := $(ENVTEST_DIR)/_matrix
TOOLS_DIR := hack/toolsenvtest is the environment controller-tools and the wider controller stack use for tests, and the fact that this repository carries a dedicated directory for it, plus a matrix subdirectory, plus a separate release line for the binaries, tells you that the test setup is part of the project's own versioning problem rather than an afterthought.
The `envtest-releases.yaml` file at the top level is the manifest of those test binaries, and the recent release list includes a tag for envtest alongside the controller-tools tags, at v1.37.0 on 2026-08-27. So the test control plane binaries are released on their own cadence, matched to the Kubernetes version rather than to the controller-tools version.
That arrangement is the practical answer to a question every controller project hits. Unit tests that need a real API server need the API server binaries, and pulling them from the network during every test run makes builds slow and flaky. Shipping a manifest of pinned versions means the test environment is described in the repository, and the same pinning story applies to the linting tool, which the Makefile resolves to a specific golangci-lint binary under the tools directory rather than whatever is installed on the machine.
The `GO_INSTALL := ./hack/go-install.sh` line points at the script that installs those pinned tools, and the `hack/` directory is also where the module cache warming and other build plumbing lives.
Working on controller-tools itself
The contributor instructions are two sentences, and they are worth reading because they describe the intended loop.
Clone this project, and iterate on changes by running `./test.sh`.Everything else is in the files. `test.sh` is the entry point for iterating, and the Makefile wraps the same work with the pinned tool versions and the environment setup described above. The repository also carries a `.golangci.yml` for linting configuration and a `codecov` style setup is implied by the tooling directory.
The release process is documented separately, with `VERSIONING.md` referenced from the README and a `RELEASE.md` in the tree, which is how a project with a strict version compatibility policy keeps that policy in writing rather than in a maintainer's head.
The maintenance picture for the project itself is healthy. The repository is not archived, and the last push was on 2026-10-19, four days after the v0.23.0 release on 2026-10-15 and two weeks after v0.22.0 on 2026-09-02. A minor release roughly every few weeks is a fast cadence for a project with this coupling, and it is the pace the upstream Kubernetes release cycle sets rather than something the maintainers choose.
The community structure is the standard Kubernetes one. Participation is governed by the Kubernetes code of conduct, the project is a subproject of kubebuilder in sigs apimachinery, and the discussion happens on the kubebuilder Slack channel and the kubebuilder Google Group, which means questions about controller-tools sit in the same place as questions about the scaffolding that generates projects using it.
Editorial conclusion
controller-tools is the right dependency for a team writing Kubernetes controllers in Go with the controller-runtime stack, and it is the sensible choice over hand-rolled code generation because its output follows the upstream conventions. Skip it if you only need to read manifests, and expect a version decision whenever the client-go version moves, since the compatibility pairing is deliberate. Verify first which controller-tools minor matches the client-go minor you are on, which Go version that requires, and which of the two the kubebuilder scaffolding you started from expects.
Frequently asked questions
Which controller-tools version should I use with my client-go version?
The README says one minor version of controller-tools is created for each minor version of client-go and the other k8s.io dependencies, and that a different pairing may work but is neither supported nor tested. The table maps CT v0.21 to k8s.io v0.36, CT v0.20 to v0.35 and CT v0.19 to v0.34.
What Go version does controller-tools require?
The README says the minimum Go version is the highest minimum Go version of the Go dependencies, and the current `go.mod` declares `go 1.26.0`. The table shows CT v0.21 requiring Go 1.26, v0.20 requiring 1.25 and v0.19 requiring 1.24.
How do I work on controller-tools itself?
The README says to clone the project and iterate on changes by running `./test.sh`. The Makefile pins the Go toolchain, the linting binary and the envtest tooling under `hack/`, and the release process is described in `VERSIONING.md` and `RELEASE.md`.
What is envtest in the controller-tools repository?
The Makefile points at `hack/envtest` and a `_matrix` subdirectory, and the repository ships `envtest-releases.yaml` describing the test binaries, which are released on their own line, with an envtest v1.37.0 tag in the recent release history. The test environment is therefore pinned in the repository rather than downloaded at test time.
Where do I ask questions about controller-tools?
The README points to the kubebuilder Slack channel and the kubebuilder Google Group, and states that controller-tools is a subproject of the kubebuilder project in sigs apimachinery. Participation is governed by the Kubernetes code of conduct.
Official sources
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.
[](https://hysenlabs.com/projects/kubernetes-sigs-controller-tools)Was this useful?