libkv: one key-value interface over Consul, etcd, ZooKeeper and BoltDB
Distributed key/value store abstraction library
At a glance
- What is it?
- Docker's libkv is a Go library that hides the differences between distributed and local key-value stores behind a single Store interface, so an application can store metadata, register endpoints for service discovery or take distributed locks without committing to one backend. The interface is small, the compatibility matrix is published, and the README is candid about where the abstraction leaks.
- Who is it for?
- Use libkv when a Go service has to talk to a key-value store that might be Consul, etcd or ZooKeeper depending on the deployment, since the interface and the backend implementations are already written and the compatibility matrix tells you exactly what each backend supports. Do not adopt it for a new project, because it has been in place since 2014 and its own roadmap still lists nicer options and new backends as future work.
- 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 140 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library is for
libkv provides a Go native library to store metadata, and its goal is to abstract common store operations for multiple distributed or local key-value store backends. The README gives the concrete use cases: storing metadata, or using service discovery to register machines and endpoints inside a cluster. It also notes that you can easily implement a generic leader election on top of it, pointing at the separate docker/leadership repository. The intended shape of a user is a Go program that should support simple metadata storage either distributed or local, a lightweight discovery service for nodes, or a distributed lock mechanism. The library is meant to be used as an abstraction layer over existing stores, and it is especially useful if you plan to support Consul, etcd and ZooKeeper from the same codebase.
The Store interface, and what it leaves to the backend
The interface a backend has to implement, fully or partially, is small enough to read in one screen.
type Store interface {
Put(key string, value []byte, options *WriteOptions) error
Get(key string) (*KVPair, error)
Delete(key string) error
Exists(key string) (bool, error)
Watch(key string, stopCh <-chan struct{}) (<-chan *KVPair, error)
WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*KVPair, error)
NewLock(key string, options *LockOptions) (Locker, error)
List(directory string) ([]*KVPair, error)
DeleteTree(directory string) error
AtomicPut(key string, value []byte, previous *KVPair, options *WriteOptions) (bool, *KVPair, error)
AtomicDelete(key string, previous *KVPair) (bool, error)
Close()
}Two things stand out. The watch methods take a stop channel, so the caller decides when a watch ends rather than relying on garbage collection, which is the right shape for a Go library. And the atomic operations take the previous pair, which is a compare-and-swap expressed as an argument rather than as a separate method, so the caller has to supply the value it believes is current.
The compatibility matrix is the real documentation
The README publishes a table of calls against the four backends, and it is the most useful thing in the document because it shows exactly where the abstraction fails. Put, Get, Delete, Exists, List, DeleteTree, AtomicPut and Close are supported everywhere. Watch, WatchTree and NewLock are supported by Consul, etcd and ZooKeeper but not by BoltDB, because a local embedded store has no server to push changes from or to arbitrate a lock against. The README explains the split as distributed backends offering enhanced capabilities such as watches and distributed locks, while local drivers are usually used in complement to the distributed drivers to store information that only needs to be available locally. The examples live in docs/examples.go, and the README points at the docker/swarm and docker/libnetwork repositories as real users of the library for the use cases listed.
The version floors explain the feature set
The supported versions section ties each backend to a minimum, and the reasons are informative. Consul versions of 0.5.1 and later are required because the library uses Sessions with Delete behaviour for the use of TTLs, which mimics ZooKeeper's ephemeral node support, and if you do not plan to use TTLs you can use Consul 0.4.0 or later. Etcd 2.0 and later is required because it uses the new coreos/etcd client, and the README notes this might change in the future as support for APIv3 arrives. ZooKeeper 3.4.5 and later is required, with the caveat that it might work with an earlier version but that this remains untested. BoltDB has no version dependency, which the README notes should not be subject to any.
Where the abstraction leaks
There is a Limitations section, and it is worth reading because it names the failure modes you will hit. Distributed key-value stores often have different concepts for managing and formatting keys and their associated values, and although libkv tries to abstract those stores aiming for some consistency, in some cases it cannot be applied easily. The compatibility document in the docs folder lists the special cases for cross-backend compatibility. Beyond those, you should expect the same experience for basic operations like Get and Put. The example given for the subtler differences is WatchTree, which may return different events or a different number of events depending on the backend, with etcd and Consul likely returning more events than ZooKeeper, which the README says you should triage properly.
TLS is a two-backend feature
The TLS section is short and the limitation is clear: only Consul and etcd have support for TLS, and you should build and provide your own config.TLS object to feed the client. Support is planned for ZooKeeper. So if your requirement includes encrypted transport to a ZooKeeper ensemble, the abstraction does not cover it today, and that is a decision you have to make before you adopt the library rather than after, because the interface itself has no TLS parameter and the workaround is to construct a client yourself for those backends. The same pattern shows up elsewhere in the library, where behaviour that differs between stores is pushed into the backend implementation rather than surfaced as an option.
The roadmap tells you the age of the design
There is a Roadmap section with four items, and reading them tells you where the project is. Make the API nicer to use using options. Provide more options, with consistency given as the example. Improve performance, by removing extras Get and List operations. Better key formatting. And then, as a question rather than a plan, new backends. Two of these, the options work and the consistency option, are about the API being dated, and the performance item is about removing operations rather than adding them, which is the opposite of where the ecosystem went. The repository layout matches a small library from that era: libkv.go and its test at the root, a store directory holding the backend implementations, a docs directory, a script directory, a testutils directory and a MAINTAINERS file. The last push to the repository was on 2026-06-04 and there are no GitHub releases, so the version history lives in the module proxy rather than in tags.
Where libkv is the wrong choice
Four limits apply. The library is a wrapper, so it can only expose what its backends share, and the ones it cannot share are documented rather than hidden, which is honest but still means your application inherits those differences wherever the abstraction leaks. It carries no TLS story for ZooKeeper and no watches or locks for the local BoltDB driver, so the choice of backend constrains your design. The API predates the context and options conventions that later Go libraries settled on, and the roadmap confirms that the ergonomics work is unfinished. And the age of the code matters for a dependency: this was written in 2014 and the copyright line runs to 2016, so if you are starting a new project you would be adopting an interface whose shape no longer matches what a Go developer in 2026 would write, with the maintenance burden carried by your team.
Editorial conclusion
Use libkv when a Go service has to talk to a key-value store that might be Consul, etcd or ZooKeeper depending on the deployment, since the interface and the backend implementations are already written and the compatibility matrix tells you exactly what each backend supports. Do not adopt it for a new project, because it has been in place since 2014 and its own roadmap still lists nicer options and new backends as future work. Verify first which backend you will actually run, since only Consul and etcd support TLS and the local BoltDB driver implements no watches and no locks, and that the special cases in the compatibility document do not affect the operations your application depends on.
Frequently asked questions
What is libkv and what is it used for?
It is a Go library that abstracts common store operations across multiple distributed and local key-value backends. It is meant for storing metadata, for lightweight service discovery where you register machines and endpoints in your cluster, and for distributed locking, and a generic leader election can be built on top of it.
Which key-value backends does libkv support?
Consul, etcd and ZooKeeper as distributed stores, and BoltDB as a local store. Consul needs version 0.5.1 or later for TTL support, though 0.4.0 works if you do not use TTLs, etcd needs 2.0 or later, and ZooKeeper needs 3.4.5 or later.
Which operations are not supported by every libkv backend?
Watch, WatchTree and NewLock are supported by Consul, etcd and ZooKeeper but not by BoltDB, since the local store has no server to push events from or arbitrate locks against. Put, Get, Delete, Exists, List, DeleteTree, AtomicPut and Close work everywhere.
Does libkv support TLS?
Only Consul and etcd do, and you build and provide your own config.TLS object to feed the client. Support for ZooKeeper is planned but not implemented, and the README does not describe a way to make the difference invisible to callers.
What licence is libkv released under?
The code is released under the Apache 2.0 licence, with the copyright held by Docker, Inc. The README and the files in the docs folder are under the Creative Commons Attribution 4.0 International licence, and the repository root carries LICENSE.code and LICENSE.docs to match.
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/docker-libkv)Was this useful?