Self-hosted service
kubernetes-sigs/controller-runtime avatar
kubernetes-sigs/controller-runtime

controller-runtime: the library kubebuilder is built on

Repo for the controller-runtime subproject of kubebuilder (sig-apimachinery)

2,973 stars1,325 forksGoApache-2.0

At a glance

What is it?
A set of Go libraries for writing Kubernetes controllers, versioned one minor release per Kubernetes minor release, with breaking changes allowed between them.
Who is it for?
controller-runtime is best understood as the framework underneath two tools most Go Kubernetes developers already use, since Kubebuilder and the Operator SDK both build on it, and adopting it directly usually means going around them rather than past them. Its versioning policy is the thing to internalise: a minor release per Kubernetes minor release, with breaking changes permitted between them and never within a patch.
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 4 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 9, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A library underneath two tools you probably already know

The README describes controller-runtime as a set of Go libraries for building Controllers, and then names who uses it: Kubebuilder and the Operator SDK, both of which the README points to as good places to start for new projects. Kubebuilder's quick start is the linked entry point for seeing it in use.

That relationship is the most important structural fact about the project. You normally arrive at controller-runtime through one of those two tools rather than choosing it directly, and the generator decides your dependency version for you. Someone adopting it directly is either building something more general than an operator, or replacing the scaffolding.

The documentation links in the README are all pkg.go.dev examples rather than prose: a package overview, a basic controller using the builder, creating a manager, creating a controller. That is a signal about the audience. This is a library with godoc examples doing the explaining, which works because the API is builder-shaped and the example functions are runnable.

The repository also carries a `designs/` directory and an `examples/` directory with several distinct programs, including builtins, a CRD example, multiclustersync, priorityqueue, a scratch environment and a token example. A design document directory in a library this size is unusual and worth exploring when the godoc leaves a question open.

Versioning that promises breaks and then keeps them

The README summarizes its own policy in a TL;DR section, and the users half reads: the project sticks to a zero major version, publishes a minor version for each Kubernetes minor release, allows breaking changes between minor versions, and publishes patch versions as needed without allowing breaking changes in them.

A zero major version with permitted breaks on every minor release is unusual for a library at 2,958 stars, and it is a deliberate consequence of tracking Kubernetes. The compatibility table in the README makes the consequence concrete, listing each controller-runtime minor against the matching k8s.io and client-go minor and a minimum Go version.

The table runs from CR v0.25 at client-go v0.37 requiring Go 1.26 down to CR v0.15 at client-go v0.27 requiring Go 1.20. The Go floor rises by one point roughly every few releases, which means your minimum Go version is effectively a function of which controller-runtime minor you pick. The README also states that the minimum Go version is the highest minimum of its dependencies, usually identical to that of the corresponding k8s.io dependencies, and that exact values can be looked up in `go.mod`.

The compatibility section adds a caution worth keeping: a minor version may happen to work with other client-go versions, but that is by chance and neither supported nor tested.

Read-your-writes, and the patch release that fixed it

Version 0.25.0, published 2026-09-03, introduced the headline feature of the line: an experimental `ReadYourWritesConsistency` option that ensures all writes are reflected in subsequent reads from the default cache-backed client.

The rationale stated in the release notes is worth reading in the maintainers' own terms. Stale client reads are described as arguably the biggest source of friction and sometimes bugs for controller authors, and providing this at the library level eliminates that class of problems entirely. Enabling it is a single manager setting, `Client.EnableReadYourWritesConsistency`, set to a pointer to true, with feedback invited on a tracking issue.

The cache is the mechanism behind the problem. A controller reads from a local cache for throughput, and that cache is eventually consistent with the API server, so a controller that creates an object and immediately reads it back can miss. The usual workaround is to read uncached for that particular object, which costs an API call and complicates the code. Making it a library-level option means the read-after-write case is handled once instead of at every call site.

Then there is what happened next. Version 0.25.1, on 2026-09-14, is a patch release containing two cherry-picked fixes: a data race on a slice in the priority queue's log state, and a resource version parse error on subresource create under read-your-writes consistency. So the feature shipped in a minor release, was marked experimental, and had a correctness bug fixed within two weeks. Both fixes came through the release cherry-pick robot, which is how a supported release line stays supported.

What else v0.25 changed, and what it broke

The v0.25.0 notes separate highlights, breaking changes and new features, and the breaking section is short but load bearing: a bump to k8s.io v1.37 across the dependency set, and a new EventRecorder interface.

The EventRecorder change is the one that will surface in your code. Replacing an interface in a library is a compile-time break for every consumer that implements or calls it, which is the cost of the policy stated earlier.

The feature list is more varied. The client gained the read-your-own-writes client. The fake client, which is what controller tests run against, gained a global resource version counter and scale subresource support for Apply, both of which make tests behave more like the real API server. Metrics gained opt-in for client-go REST client metrics and the ability to override the latency histogram buckets. A new Source type was added as well.

The fake client work deserves a note because it signals where the project's attention is going. Test fidelity is the thing that makes a controller library usable, since without a fake client close enough to real semantics, a test suite either becomes slow integration tests or becomes a test of a mock.

Between v0.24.1 in May 2026 and v0.25.0 in September, the notable v0.24.1 entry is a fix for a regression in Apply typed error handling, also cherry-picked to the release branch.

Reading the metadata against the project description

The repository description field reads as Repo for the controller-runtime subproject of kubebuilder (sig-apimachinery), which is inaccurate on two counts and worth correcting before you rely on it. controller-runtime is not a subproject of kubebuilder; the README says the opposite, that kubebuilder and the Operator SDK leverage controller-runtime. And controller-runtime belongs to kubernetes-sigs with the topic `k8s-sig-api-machinery`, not to the api-machinery repository itself.

Aside from that, the metadata is unremarkable in a good way: Apache-2.0, not archived, default branch `main`, 2,958 stars, 1,319 forks and 66 open issues, with the last push on 2026-09-23.

The fork count is roughly 45 percent of the star count, which is high even by dependency standards. For a library that sits underneath generators, that ratio fits: forks come from teams vendoring controller-runtime into an internal platform rather than from casual interest.

The tree supports the reading of a mature, well-documented project: `FAQ.md`, `VERSIONING.md`, `RELEASE.md`, `TMP-LOGGING.md`, `OWNERS` and `OWNERS_ALIASES`, a `.golangci.yml` and a `.gomodcheck.yaml`. The dependency policy file is the more interesting of the two lint configs, since it governs how much of the Kubernetes dependency surface the library is allowed to pull in.

Editorial conclusion

controller-runtime is best understood as the framework underneath two tools most Go Kubernetes developers already use, since Kubebuilder and the Operator SDK both build on it, and adopting it directly usually means going around them rather than past them. Its versioning policy is the thing to internalise: a minor release per Kubernetes minor release, with breaking changes permitted between them and never within a patch. Version 0.25.1 is the current release, dated 2026-09-14, and it exists to fix two bugs in the experimental read-your-writes client introduced ten days earlier.

Frequently asked questions

What is a controller in Kubernetes?

A controller is a program that watches the state of resources in a cluster and acts on it, reconciling what is there against what should be there. controller-runtime is the Go library that provides the pieces for writing one: a manager that runs shared machinery, a controller that holds the reconcile loop, and sources that deliver the events triggering each reconcile.

Why does controller-runtime follow a zero major version with breaking minor releases?

Because it tracks Kubernetes itself. The README states that the project publishes a minor version for each Kubernetes minor release and allows breaking changes between minor versions, while patch versions never contain breaking changes. Version 0.25 bumps k8s.io to v1.37 and adds a new EventRecorder interface as a result.

What does the ReadYourWritesConsistency option do?

It ensures writes are reflected in subsequent reads from the default cache-backed client, which addresses stale reads from the informer cache after a create or update. Version 0.25.0 introduced it as experimental, enabled by setting `Client.EnableReadYourWritesConsistency` to a pointer to true on the manager, with feedback collected on a tracking issue.

How does controller-runtime relate to Kubebuilder and the Operator SDK?

It is the library underneath both of them. The README states that controller-runtime is leveraged by Kubebuilder and the Operator SDK, and points to both as good places to start a new project, with Kubebuilder's quick start as the worked example. Practically, this means the generator you choose pins your controller-runtime version for you.

What Go version does a given controller-runtime release require?

The README publishes a compatibility table pairing each controller-runtime minor with its k8s.io and client-go minor and a minimum Go version, running from CR v0.25 at Go 1.26 back to CR v0.15 at Go 1.20. The stated rule is that the minimum Go version is the highest minimum of its dependencies, and exact values can be read from the repository's `go.mod`.

Official sources

  1. Issues
  2. kubernetes-sigs/controller-runtime on GitHub
  3. License: Apache-2.0
  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/kubernetes-sigs-controller-runtime.svg)](https://hysenlabs.com/projects/kubernetes-sigs-controller-runtime)