The NATS Key-Value store is surprisingly capable of acting as a distributed, strongly consistent, replicated database, not just a simple cache.

Let’s see it in action. Imagine we have a NATS server running with JetStream enabled. We can create a Key-Value store named my_kv_store directly from the nats CLI:

nats kv add my_kv_store

This command initializes a new Key-Value store. Once created, we can put a value into it:

echo "hello world" | nats kv put my_kv_store greeting "hello world"

And then retrieve it:

nats kv get my_kv_store greeting

Output:

hello world

This looks simple, but behind the scenes, NATS JetStream is doing a lot. When you put a key, NATS doesn’t just store it locally. It replicates this change across a cluster of NATS servers using the Raft consensus algorithm. This replication ensures that even if some servers go down, your data remains available and consistent.

The core problem the Key-Value store solves is providing a simple, yet robust, way to store and retrieve small pieces of configuration, state, or metadata in a distributed system. Instead of complex database setups, you can leverage your existing NATS infrastructure.

Internally, each Key-Value store is implemented as a special type of JetStream stream. This stream is configured with specific settings that enable the Key-Value semantics. When you put a key, NATS creates a new entry (a message) in this underlying stream. This message contains the key, the value, and metadata like a revision number. When you get a key, NATS looks up the latest message associated with that key in the stream. The "store" aspect comes from JetStream’s ability to retain only the latest value for each key, discarding older versions implicitly.

The key levers you control are primarily within the JetStream stream configuration that backs the KV store. When you create a KV store, you can specify:

  • Replication Factor: How many NATS servers should hold a copy of the data for fault tolerance.
  • Storage Type: memory, file, or rocksdb. For persistence, you’ll want file or rocksdb.
  • Retention Policy: While KV semantics inherently keep the latest, you can set stream-level retention (e.g., interest or limits) that might affect older, non-current values if you were to inspect the underlying stream directly.
  • Discard Policy: old (default for KV, keeps latest) or new (ignores messages if a newer version of the key exists).

The most surprising aspect is how NATS handles conflicting updates when a cluster is partitioned. If two different NATS servers receive updates for the same key while they are temporarily isolated, JetStream’s Raft implementation will ensure that only one of those updates ultimately becomes the canonical "latest" value once the partition heals. It doesn’t simply pick one arbitrarily; it uses the versioning (revisions) and the consensus protocol to deterministically resolve the conflict, ensuring strong consistency across the cluster. This means you don’t get "last writer wins" in a way that can lead to data loss; you get "consistent writer wins" according to the consensus.

The next concept to explore is how to use the KV store for dynamic configuration updates across a fleet of services.

Want structured learning?

Take the full Nats course →