envoyproxy/go-control-plane: the shared xDS server library behind custom Envoy control planes
Go implementation of data-plane-api. Instead, it provides infrastructure that is shared by multiple different control plane implementations.
At a glance
- What is it?
- go-control-plane is not a control plane. It is the Go infrastructure (gRPC xDS server, configuration caches, generated protos) that other control planes are built on, and the README is explicit that platform-specific translation is out of scope.
- Who is it for?
- Adopt it if you are writing a Go control plane that speaks xDS and you want the gRPC server, proto types and cache semantics handled for you; the README says the API server is meant to be imported as is in production deployments. Do not adopt it if you expected a running control plane, or if you need it to convert your service registry into Envoy config, which the README places outside the repository's scope.
- 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 5 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 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What go-control-plane actually is, and who should not install it
The name misleads. The README states plainly that this code base "does not attempt to be a full scale control plane for a fleet of Envoy proxies," and that because platforms differ, no single control plane implementation can satisfy everyone. What the repository ships is shared infrastructure: a generic gRPC API server implementing the xDS APIs defined in data-plane-api, an in-memory configuration cache, and the Go proto types synced from the upstream Envoy repository. The intended consumer is a team writing its own control plane in Go, importing this library rather than reimplementing the discovery protocol.
That framing also defines who is out of scope. If you want a binary you point at a service registry and forget about, this is the wrong repository. The README says the project will not tackle translating platform-specific representations of resources (services, instances of services) into Envoy-style configuration, and that this aspect might be revisited based on usage and feedback. Until then, the translation layer is your code, and it is usually the larger half of the work.
The xDS data flow: your cache, the gRPC server, and ephemeral Envoys
The architecture visible in the README is a push pipeline with a cache in the middle. Your control plane populates the cache; the API server answers Envoy discovery requests from it; Envoys connect over gRPC and receive configuration updates. The README's stated reason for the cache is that Envoy clients are assumed to be ephemeral and can come and go arbitrarily, so the server uses the cache to minimize client load on the server. Populating and invalidating that cache is explicitly the consumer's responsibility, not the library's.
Cache keys are not arbitrary. The README says the cache is keyed on a pre-defined hash function whose keys are based on the Node information defined in base.proto. That means the identity an Envoy sends in its Node field determines which group of proxies shares a snapshot, so a misconfigured Node identifier silently splits or merges configuration groups. This is the kind of detail that is easy to get wrong once and hard to debug later.
Three cache implementations are documented. Simple is snapshot-based and maintains a consistent view per group of proxies; it can run as an ADS server or as disaggregated xDS servers, and in ADS mode it can hold responses until the complete set of referenced resources is requested (the README's example is the entire set of RDS referenced by LDS), which is what makes an atomic update of xDS collections possible. Linear is eventually consistent, scoped to a single type URL collection, and keeps a single linear version history plus a version vector; it compares the request version against the latest versions for the requested resources and responds with whatever changed. The README notes this cache assumes resources are entirely opaque. Mux is a combinator: it lets you mix caches per type URL, for instance Simple for LDS, RDS and CDS and Linear for EDS.
Installing go-control-plane and getting the example server running
The README lists one requirement: Go 1.26 or newer, which matches the go directive in go.mod. The recommended way to validate a checkout is the Docker-based test target, because the README says it runs the tests in the same environment as CI and therefore produces a consistent set of generated files. Expect a container build and a full test run, not a quick smoke test.
make docker_testsAfter that, the README points to the example server under internal/example as the place to see how the library is integrated into a program. That directory is the intended starting point for understanding the API surface, rather than any top-level documentation.
To depend on the library from your own module, the module path is github.com/envoyproxy/go-control-plane, and go.mod shows the envoy and ratelimit submodules are wired in through replace directives pointing at ./envoy and ./ratelimit. The proto types live in the envoy submodule, and the require block pins github.com/envoyproxy/go-control-plane/envoy at v1.37.0 and github.com/envoyproxy/go-control-plane/ratelimit at v0.1.0. If you vendor or mirror modules, those two paths are what you need alongside the root module. The repository also demonstrates a more complete integration in examples/dyplomat, which the Makefile builds through the examples target. The README does not document a rollback procedure for cache state or a migration path between cache implementations, so treat the choice of Simple versus Linear as an architectural decision you make before writing the control plane, not something you swap later.
Version coupling: proto sync, xDS versions, and the removed V2 code
The Go proto files are synced from the upstream Envoy repository on every upstream commit, triggered by the envoy-sync.yaml workflow. That keeps types current with Envoy but also means the library's surface moves with upstream. Pinning a version in your go.mod is the only stable reference you get; the README offers no compatibility matrix beyond the versioning scheme it links to in the Envoy documentation.
V2 is gone. The README states that V2 control-plane code has been removed and will no longer be supported, and that anyone who still needs it should use a previous SHA. There is no deprecation shim described. If you maintain a control plane written against V2 types, the upgrade is a rewrite of the type usage, and the README's only pointer is backward to an older commit.
The release tags follow the envoy submodule versioning: envoy/v1.39.0 and envoy/v1.38.0 were both tagged on 2026-08-17, with envoy/v1.37.0 earlier on 2026-02-16. The last push to the repository was on 2026-08-17. Two tags sharing a date suggests the root module and the envoy submodule were tagged in the same pass, so when you pin a version, check which module the tag belongs to before assuming the root library changed.
Where go-control-plane is the wrong tool
The clearest failure mode is scope confusion. A team that installs this expecting a working control plane will find a library, an example server and a test harness. The README's scope section is unambiguous that resource translation is not handled, so the moment your requirement is "read services from Consul, Kubernetes or a database and turn them into clusters and listeners," you are writing that code yourself, and the library only helps once you have Envoy-shaped resources in hand.
The cache design is the second constraint. Because the library delegates population and invalidation to the consumer, correctness depends on your code deciding when a snapshot is stale. The README gives no invalidation policy, no TTL and no consistency guarantee beyond what each cache type provides: Simple is snapshot-based per proxy group, Linear is eventually consistent for one type URL collection and treats resources as opaque. If your platform needs cross-type transactional updates, the ADS hold behaviour in Simple is the mechanism the README describes for that, and it only applies in ADS mode.
Finally, the ephemeral-client assumption shapes everything. The cache exists to reduce load from clients that come and go. If your Envoys are long-lived and few, the caching layer buys less than it costs in complexity, and you may be better served by a much thinner server. The README does not offer a no-cache mode.
How it differs from java-control-plane and hand-rolled xDS servers
The nearest comparison is Envoy's java-control-plane, the Java counterpart to this library. Both implement the same xDS discovery APIs from data-plane-api and both leave platform translation to the consumer, so the difference is mostly ecosystem: go-control-plane is for teams whose control plane is already Go, and java-control-plane for teams on the JVM. The proto types in both cases are generated from the same upstream definitions, which is why the README can point at data-plane-api and the Envoy versioning document rather than restating the protocol.
A hand-rolled xDS server is the other alternative, and it is more tempting than it looks. The discovery protocol itself is a set of gRPC streaming services, and a minimal server can be written in a few hundred lines. What you would be rebuilding is the cache semantics: the Simple cache's snapshot consistency per proxy group and its ADS hold-until-complete behaviour, and the Linear cache's version vector diffing for a single type URL. Those are the parts where subtle bugs produce Envoys that are quietly serving stale configuration. The trade-off is real in the other direction too: importing the library means inheriting its Node-based cache keying and its release cadence, which tracks upstream Envoy rather than your own schedule.
Licence, maintenance and upgrade cost
The repository is Apache-2.0, the same licence Envoy itself uses, which matters if you are linking this into a control plane you distribute: Apache-2.0 carries an explicit patent grant and does not impose copyleft on your own code. This is a description of the licence text, not legal advice; if you redistribute modified versions, read the attribution and notice requirements yourself.
Maintenance signals are concrete rather than implied. The repository is not archived, the last push was on 2026-08-17, and releases are cut against the envoy submodule, with envoy/v1.39.0 and envoy/v1.38.0 tagged on 2026-08-17 and envoy/v1.37.0 on 2026-02-16. The proto sync workflow runs on every upstream Envoy commit, so generated types track upstream continuously even between your own dependency bumps.
The upgrade cost is dominated by that coupling. Bumping the envoy submodule brings new proto types and, occasionally, removals like the V2 code. The README does not describe a deprecation window or a migration guide for type changes, so budget for reading the generated diffs rather than release notes. The Makefile also shows the test target runs with -race, a 30s timeout and -parallel 100, and carries a TODO noting the parallel setting exists because of a known concurrency test; that is a hint that the cache code has had concurrency-sensitive behaviour worth testing against in your own integration.
Editorial conclusion
Adopt it if you are writing a Go control plane that speaks xDS and you want the gRPC server, proto types and cache semantics handled for you; the README says the API server is meant to be imported as is in production deployments. Do not adopt it if you expected a running control plane, or if you need it to convert your service registry into Envoy config, which the README places outside the repository's scope. Before writing code, read internal/example/README.md, decide whether you need the Simple cache's ADS hold-for-completeness behaviour or the Linear cache's version-vector diffing, and check whether the xDS version you target is still generated, since V2 code has been removed and the README points to a previous SHA for it.
Frequently asked questions
Is envoyproxy/go-control-plane a full control plane for Envoy?
No. The README states the code base does not attempt to be a full scale control plane for a fleet of Envoy proxies, and instead provides infrastructure shared by multiple control plane implementations, including a gRPC xDS API server and a configuration cache.
What Go version does go-control-plane require?
The README lists Go 1.26 or newer under Requirements, and go.mod declares go 1.26.0.
Which configuration caches does go-control-plane provide?
The README documents three: Simple, a snapshot-based cache with a consistent per-group view that can hold ADS responses until all referenced resources are requested; Linear, an eventually consistent cache for a single type URL collection with a version vector; and Mux, a combinator that mixes caches per type URL.
Does go-control-plane translate my services into Envoy configuration?
No. The README says the repository will not tackle translating platform-specific representations of resources such as services and instances of services into Envoy-style configuration, and that this may be revisited later based on usage and feedback.
Is the V2 xDS control-plane code still supported?
No. The README states that V2 control-plane code has been removed and will no longer be supported, and recommends using a previous SHA if V2 is still needed.
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/envoyproxy-go-control-plane)