blog by OSO

Productionise Kafka Streams State Stores: How Unbundled Architecture Transforms Kafka Streams Operations

Sion Smith
Sion Smith 6 July 2025
OSO guide to running Kafka Streams state stores in production with unbundled state, white title text on a navy panel with a lime Blogs label.

Updated November 2026: We have reworked this guide into a question-led format, added the Kafka Streams settings that soften the warm-up penalty today, a comparison of bundled and unbundled state, and a short FAQ. The facts were checked against the Apache Kafka 4.4 documentation and the current SlateDB project.

Stateful Kafka Streams applications promise elasticity, fault tolerance and real-time processing. Yet when you need to scale during a traffic spike or debug a production incident, those benefits often evaporate: the application sits rebuilding gigabytes of state from changelogs while consumer lag grows. That is not a flaw in Kafka Streams itself. It is a consequence of bundling state with compute.

OSO engineers work with many teams running Kafka Streams in production, and we see a clear pattern: the most mature streaming platforms are moving towards unbundled state, where state management is decoupled from application compute. This guide explains why, how it works and what to weigh before you adopt it.

Unbundled Kafka Streams state: the Kafka Streams application uses an RS3 state store, which talks to the RS3 service running SlateDB on top of Amazon S3
Unbundled state stack: Kafka Streams, a remote state service on SlateDB, and Amazon S3
ConcernBundled state (local RocksDB)Unbundled state (object storage)
Scaling outNew instances replay changelogs before processingNew instances read shared state almost immediately
Recovery after failureRebuild the store from the changelog topicReopen the store from durable object storage
DebuggingInteractive Queries or manual changelog scansQuery snapshots without touching the live application
Fixing corrupted stateReset offsets, wait for windows to expire or edit RocksDBRoll back to a known-good snapshot
Testing with real dataSynthetic data sets or full replaysClone production state into a test application
Latency per state operationLocal disk, sub-millisecondNetwork hop, mitigated with caching

Why are Kafka Streams state stores hard to run in production?

Kafka Streams state stores are hard to run because state lives on the same instances as compute, so every scale-out, restart or bug fix is limited by how fast that state can be rebuilt or repaired.

What is the warm-up penalty?

The warm-up penalty is the time a new instance spends restoring state before it can process records. By default, Kafka Streams stores state locally in RocksDB, and each store is backed by a changelog topic. When you add instances during a traffic burst, they must consume their assigned changelog partitions before taking over active work. OSO engineers have measured this taking anywhere from tens of minutes to several hours for applications with large state, which cancels out the elasticity that drew teams to streaming in the first place.

Picture a fraud detection system during a Black Friday peak. You need more compute to handle the load, but new nodes cannot help until restoration finishes, and by then the spike has often passed.

How can you reduce the warm-up penalty today?

You can reduce it with standby replicas and warm-up replicas, both built into Kafka Streams. KIP-441 changed task assignment so that a stateful task stays active on a caught-up instance while a new instance warms up in the background. The relevant settings, as documented in the Kafka Streams configuration reference, are:

  • num.standby.replicas: shadow copies of each local store kept up to date on other instances. The default is 0; the documentation recommends 1 for resilience.
  • max.warmup.replicas: how many extra replicas can warm up at once. The default is 2.
  • acceptable.recovery.lag: how far behind (in changelog offsets) an instance can be and still receive an active task. The default is 10,000.
  • probing.rebalance.interval.ms: how often Streams rebalances to check whether warm-up replicas have caught up. The default is 10 minutes.

These settings hide restoration time rather than removing it. Standbys double or triple your state storage and still need to replay changelogs when they first start. For a deeper look at how assignment works, see our guide to Kafka Streams rebalances and assignments, and size RocksDB carefully with our Kafka Streams memory best practices.

Why is debugging stateful applications so slow?

Debugging is slow because state is locked behind stream threads. Unless you have built a serving layer on Interactive Queries, engineers resort to changelog archaeology: scanning topics by hand to find when bad data entered an aggregate. OSO engineers have seen teams spend days isolating the moment state went wrong. Kafka holds a complete audit trail of what happened, but seeing a historical view of state normally means rebuilding the store from scratch.

Why doesn’t deploying a bug fix repair corrupted state?

Deploying a fix does not repair state because the output of a Kafka Streams application is a function of its code, its inputs and its state. If the state is already polluted, correct code keeps producing wrong results. The traditional options are painful: reset to the start of the input topics if retention allows, wait for windowed aggregates to expire, or edit RocksDB directly. Teams need a way to return to a known-good state while keeping the benefits of an event-driven design.

How does an unbundled state architecture work?

An unbundled state architecture moves state stores out of the application into a separate state service that persists data to object storage, while keeping the Kafka Streams programming model unchanged.

Why is object storage a good foundation?

Object storage is a good foundation because it is highly durable, scales without capacity planning and offers strong consistency with conditional writes. Combined with Kafka’s event log, those properties are enough to build durable, shareable state. Object storage also supports shared access: many readers can scan the same objects without slowing the primary writer, so state can be queried and analysed independently of the application.

What are the three tiers?

Unbundled state systems usually separate three tiers:

  • Application tier: your Kafka Streams application runs with wrapped consumers, producers and state stores that talk to the state service instead of local RocksDB.
  • State service tier: a service that implements the Kafka Streams state store interface, persists data to object storage, replicates changelog semantics and adds capabilities such as snapshots.
  • Storage tier: durable object storage, such as Amazon S3, holding state in a format that suits both streaming access and analytical reads.

This split lets you scale compute and storage independently while keeping the processing semantics Kafka Streams applications expect.

Unbundled state layers: a Kafka Streams processor with an RS3 state store and record cache, the RS3 protocol and SlateDB in the state service, and S3 underneath
Layers of an unbundled Kafka Streams state store

How does the application talk to the state service?

The protocol mirrors Kafka’s changelog approach: writes are acknowledged first and flushed to durable storage asynchronously. State updates collect in a write buffer inside the state store client. On commit, the service receives the relevant changelog offsets and processes them in order to stay consistent. Because the service acknowledges writes before the expensive object storage write completes, the application keeps processing. The service tracks which offsets are durably flushed, which lets it truncate changelogs and gives you recovery points for later.

What can snapshots and clones do for Kafka Streams operations?

Snapshots and clones turn state into something you can inspect, copy and roll back, which makes debugging, testing and recovery far faster.

What makes a snapshot consistent?

A snapshot is consistent when it captures all application state that matches one set of committed offsets across the source topics. That is harder than it sounds in topologies with repartitioning. If task A processes a record and writes derived data that task B later reads from a repartition topic, a valid snapshot must include both A’s processing and B’s downstream effect, or neither. Getting this right is what makes snapshots trustworthy for debugging and testing.

How do snapshots change debugging?

Snapshots let engineers query historical state directly, without affecting the running application. When results look wrong, you can check whether a store holds corrupted data and then binary-search across snapshots to find exactly when the corruption appeared. Automated tools can evaluate a predicate against each snapshot to narrow the window. You get both a timeline for the investigation and the last known-good state for recovery.

What is clone-based testing?

Clone-based testing means starting a new application instance from a historical snapshot, including its state and consumer offsets. Teams can test new features against real production state without touching live systems, rather than relying on synthetic data that never quite matches production. For incident work, a clone reproduces the exact conditions behind a bug, so engineers can step through it in a debugger.

Why do LSM trees suit object storage?

LSM trees suit object storage because they write data in large, immutable batches, which is exactly the access pattern object storage handles cheaply and quickly.

Object storage is good at large sequential writes and penalises small random operations in both latency and request cost. It also does not support in-place updates, which rules out B-tree designs. An LSM tree accumulates writes in memory and flushes them as large immutable files, then compacts those files in the background. RocksDB, the default Kafka Streams store, is itself an LSM tree, just one designed for local disk.

How does SlateDB store state on object storage?

SlateDB is an open-source, Apache 2.0 licensed embedded key-value database and LSM engine built on object storage. It runs on Amazon S3, Google Cloud Storage, Cloudflare R2 and most other object stores. Data is held in sorted string tables (SSTs), immutable files of sorted key-value pairs. When the in-memory memtable fills, it is written out as a new SST object.

The database state at any moment is a set of SSTs, tracked by a versioned manifest. Adding SSTs creates a new manifest version, so a checkpoint is simply a reference to a manifest version. Checkpoints therefore take constant time and space whatever the database size, which is what makes snapshots and clones practical for large production applications. SlateDB also guarantees a single writer through manifest fencing while allowing multiple readers.

How do caches keep latency down?

Caches keep latency down by serving hot data before a request ever reaches object storage. Applications keep a local cache, and the state service adds a hybrid memory and local-disk cache. Most reads never touch S3, so performance stays close to local state stores, while the shared storage underneath still allows analytical queries over the full data set.

How do you take a consistent snapshot across sub-topologies?

You take a consistent snapshot across sub-topologies with the Chandy-Lamport distributed snapshot algorithm, which uses marker records to line up every task’s snapshot with the same point in the data flow.

Unsynchronised snapshot protocol: four tasks across two sub-topologies snapshot independently, so records w and a land in the snapshot inconsistently with offsets 102 and 120
Why independent snapshots are inconsistent

Why do naive snapshots go wrong?

Naive snapshots go wrong when tasks snapshot independently. Imagine a first sub-topology that enriches orders and writes to a repartition topic, and a second that aggregates them. If each task snapshots on its own schedule, you can capture enriched records upstream without the matching aggregates downstream, which is an inconsistent view of the application.

How does marker propagation work?

When a snapshot starts, source tasks (those reading input topics directly) take a local snapshot and then inject marker records into their output topics. Downstream tasks start their snapshot when they see the first marker and finish only after receiving markers from every upstream producer. Records that arrive between the first and last markers are captured as part of the snapshot, so causal relationships are preserved.

What does it take to integrate this with Kafka Streams?

Integration needs hooks into consumers, producers and state stores to inject markers and time snapshots. The hardest part is triggering commits on demand: Kafka Streams schedules commits on a best-effort basis, but the algorithm needs a commit exactly when a marker arrives, which usually means changing default commit behaviour. If you write custom processors, our guide to the Kafka Streams Processor API covers the hooks involved, and building a custom state store shows how the store interface fits together.

How should you adopt unbundled state?

Adopt unbundled state gradually: start with new or heavily refactored applications, measure the latency cost, then move workloads that benefit most from fast scaling and recovery.

Key takeaway: Unbundling RocksDB from Kafka Streams trades a small network cost per state operation for fast scaling, snapshot-based debugging and point-in-time recovery. Tune standby and warm-up replicas first, and move to unbundled state when restoration time is the bottleneck.

What is a sensible migration path?

Migrating applications with large existing state needs planning. OSO engineers recommend starting with new applications, or ones already being refactored. For existing applications, move read-heavy workloads first to validate performance, then write-heavy processing. A staged migration lets your team build operational experience while production stays stable.

What latency should you expect?

Remote state adds a network hop to every uncached state operation, typically around 1 to 2 milliseconds within the same cloud region. For many workloads that is acceptable given the operational gains. Latency-sensitive applications can process several records concurrently within a stream thread, which turns the per-operation delay into a throughput question and often improves overall performance.

Which operational workflows does it unlock?

Teams that adopt unbundled state usually build these workflows into their release and incident processes:

  • Pre-deployment testing: clone production into staging to validate a change before release.
  • Forensic analysis: inspect historical behaviour through snapshots without touching live processing.
  • Disaster recovery: replicate snapshots across regions for fast failover with minimal data loss.
  • Feature development: test new logic against realistic state in development environments.

Where is the ecosystem heading?

The ecosystem is converging on disaggregated storage and compute. Responsive, a founding member of the SlateDB project, announced on 9 October 2026 that it is joining Databricks to work on Lakebase, citing the same ideas: state on object storage, and instant checkpoints, branches and clones. In Apache Kafka itself, KIP-892 (transactional state stores) has been accepted, which addresses the related problem of local state being wiped after a crash under exactly-once processing. If you are deciding how to run stateful streaming in production, OSO’s Kafka support and Kafka services teams can help you choose and implement the right approach.

Frequently asked questions

What is a state store in Kafka Streams?

A state store is a key-value store that a Kafka Streams application uses to remember data between records, for example for aggregations, joins and windows. By default each store is a local RocksDB instance, backed by a changelog topic in Kafka so it can be rebuilt after a failure.

Why does Kafka Streams use RocksDB?

Kafka Streams uses RocksDB because it is a fast embedded key-value store that can hold more state than fits in memory, spilling to local disk. It is an LSM tree, so writes are cheap, and it runs inside the application process with no extra service to operate.

Should I use RocksDB or a remote store for Kafka Streams state?

Use RocksDB when your state restores quickly and you rarely scale. Consider a remote store on object storage, rather than an HDFS-style shared file system, when restoration takes too long, you scale often, or you need snapshots for debugging and recovery. The trade-off is a small network cost per uncached operation.

How do standby replicas help Kafka Streams state stores?

Standby replicas keep shadow copies of each state store up to date on other instances, so a failed task can move to an instance that is already caught up instead of replaying the whole changelog. Set them with num.standby.replicas; the default is 0.

What is unbundled state in Kafka Streams?

Unbundled state means running state stores in a separate service that persists data to object storage, instead of in local RocksDB on each application instance. It decouples state from compute, so instances can scale quickly and state can be snapshotted, cloned and rolled back.

How do you fix corrupted state in a Kafka Streams application?

Without snapshots, you fix corrupted state by resetting the application to reprocess its input topics, waiting for windowed state to expire, or rebuilding the store from a clean changelog. With snapshot support, you find the last known-good snapshot and restore the application from it.

Are you looking to move your Kafka Streams apps into production?

Have a conversation with one of our experts to discover how we can work with your team to improve your Kafka Streams operations.

CONTACT US
OSO
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.