Kafka Active-Active Replication: Architecture, Setup, and Limits
Kafka active-active replication runs two or more clusters that all serve producers and consumers, with bidirectional replication keeping them converged. Every region writes locally, reads locally, and receives the other regions' data within seconds.
Kafka has no native multi-master mode, so active-active is an architecture you assemble — from a cross-cluster replication tool plus application-level discipline. Three problems define the assembly: replication loops, consumer offset divergence, and the complete absence of conflict resolution.
Active-active is not a Kafka feature you enable. It is a topology you build from bidirectional replication, local-write discipline, and offset checkpoints — and it still needs an independent backup, because replication carries corruption to every cluster at the same speed as good data.
What active-active means for Kafka
In an active-active design, every cluster accepts producer traffic. A replication flow in each direction copies records between clusters, so each cluster eventually holds both its own data and its peers'.
This is the opposite of active-passive DR, where one cluster serves everything and a standby waits idle. Active-passive buys simplicity; active-active buys failover time and regional latency.
One distinction to settle first: none of this is Kafka's replication factor. Replication factor copies partitions across brokers inside one cluster, and every replica shares that cluster's failure domain. The Kafka replication pillar covers those partition-level mechanics; this guide is about replication between live clusters.
Because Kafka ships no multi-master mode, the pattern rests on external tooling. MirrorMaker 2 is the open-source standard and the focus of this guide. Confluent Replicator and Cluster Linking offer commercial equivalents, and MSK Replicator provides a managed option on AWS — each supports bidirectional topologies with different topic-naming rules.
When active-active is worth the cost
Active-active earns its complexity in three situations. Regional users need local produce and consume latency. The business requires failover measured in seconds, not minutes. Or read load benefits from being served in more than one region at once.
Everything else argues for active-passive. Both clusters run at production scale permanently, so infrastructure cost roughly doubles. Applications must change: producers pin writes locally, consumers aggregate topics, and processing must tolerate duplicates. The operational surface — checkpoint lag, cycle monitoring, dual upgrade windows — demands a team that already runs one cluster well.
| Dimension | Active-passive | Active-active |
|---|---|---|
| Failover time | Minutes (promotion runbook) | Seconds (traffic shift) |
| Run cost | Primary + smaller standby | Two full production clusters |
| Client changes | None until failover | Local writes, aggregated reads, idempotency |
| Ordering | Per partition, one cluster | Per partition per source cluster only |
| Conflict handling | Not needed | Not provided — application's problem |
| Operational maturity | Moderate | High |
Most workloads should start active-passive and move a topic family to active-active only when latency or recovery-time requirements force it. The clusters can even coexist: many teams run active-active for a handful of customer-facing topics and active-passive for everything else.
Topic naming and loop prevention
Bidirectional replication has an obvious failure mode: cluster east copies a record to west, west copies it back to east, forever. MirrorMaker 2 prevents this with topic naming.
MM2's DefaultReplicationPolicy prefixes every replicated topic with its source cluster alias. Records produced to orders on east land in east.orders on west. MM2 recognizes the prefix as a remote topic and never replicates it back toward its origin, so a record crosses the link exactly once.
IdentityReplicationPolicy keeps topic names identical on both sides. That reads nicely, but it removes the loop protection — MM2 can no longer tell local records from replicated ones. Identity naming is only safe in one-directional topologies, which rules it out for active-active with MM2. MSK Replicator makes the same trade explicit: it offers prefixed names or an identical-name mode with its own loop-prevention built in.
| Naming strategy | Remote topic on peer | Cycle-safe bidirectionally? | Consumer impact |
|---|---|---|---|
DefaultReplicationPolicy | east.orders | Yes | Consumers subscribe to local + prefixed topics |
IdentityReplicationPolicy | orders | No — one-way only | None, but unusable for active-active MM2 |
| MSK Replicator (identical names) | orders | Yes (managed loop prevention) | None |
For self-managed active-active, accept the prefixes. They are the mechanism that makes the topology safe, and they carry useful provenance: any consumer can see which cluster a record originated on.
Bidirectional MirrorMaker 2 configuration
A two-cluster active-active flow is one mm2.properties with both directions enabled:
clusters = east, west
east.bootstrap.servers = kafka-0.east.svc:9092,kafka-1.east.svc:9092
west.bootstrap.servers = kafka-0.west.svc:9092,kafka-1.west.svc:9092
east->west.enabled = true
east->west.topics = orders.*|payments.*
west->east.enabled = true
west->east.topics = orders.*|payments.*
replication.factor = 3
sync.topic.configs.enabled = true
emit.heartbeats.enabled = true
emit.checkpoints.enabled = true
sync.group.offsets.enabled = true
checkpoints.topic.replication.factor = 3
heartbeats.topic.replication.factor = 3
offset-syncs.topic.replication.factor = 3
Three connector families do the work in each direction. MirrorSourceConnector copies the records. MirrorCheckpointConnector translates consumer group offsets and emits them to the peer cluster. MirrorHeartbeatConnector produces heartbeat records that prove the link is alive end to end — monitor their arrival on both sides.
Deployment mechanics — dedicated Connect cluster, task sizing, lag monitoring — are the same as any MM2 installation. The MirrorMaker 2 setup tutorial covers them step by step; run everything there twice, once per direction.
Producers, consumers, and the aggregation pattern
The application rules are where active-active succeeds or fails.
Producers write only to their local cluster. Replication moves the data; producers never fail over mid-write to a peer. A producer that writes the same logical stream to both clusters creates duplicate records that no downstream component can distinguish.
Consumers that need the full stream aggregate topics. On west, the complete orders stream is orders plus east.orders. A regex subscription such as .*orders picks up both, along with any future region's prefixed copy. Consumers that only need local data subscribe to the bare topic name.
Ordering shrinks to per partition, per source cluster. Within orders partition 3 on east, order holds. Between orders and east.orders on west, there is no ordering relationship at all. Any logic that assumes a global sequence across regions is broken by design, not by accident.
There is no conflict resolution. If two regions update the same key concurrently, both records exist on both clusters, in different topics, in no defined order. Kafka will not merge them, and neither will MM2. Working designs partition writes so each key has a home region — for example, users write to the region that owns their account — or they treat the streams as append-only facts where divergence is acceptable.
Cross-cluster delivery is also at-least-once. Retries in the replication path can duplicate records, so consumers need idempotent handling — the same discipline any careful Kafka consumer already has, made non-negotiable.
Consumer offsets between live clusters
Offsets are cluster-local. A group committed at offset 4,200,000 on east's orders-3 has no meaningful position on west, where the equivalent records live in east.orders-3 at different offsets.
MM2's checkpoint connector solves the translation. It emits per-group checkpoints into the peer cluster that map committed offsets to their remote equivalents. With sync.group.offsets.enabled = true, MM2 applies those translations directly to the peer's __consumer_offsets — but only for groups with no active members there, so live groups are never disturbed.
Moving a consumer group between active clusters is therefore a controlled procedure, not an automatic one. Stop the group on its home cluster and let the final checkpoint propagate. Verify the translated offsets on the peer. Restart the group there, subscribing to the aggregated topic set. Expect a replay window: records processed after the last synced commit will be processed again.
Test this both directions before trusting it, and monitor checkpoint lag as seriously as replication lag. A stale checkpoint turns a clean failover into hours of replay. The offset management guide covers the tooling for inspecting and resetting group positions when the automated path needs help.
What active-active does not solve
Active-active is an availability architecture, and it is easy to mistake it for a recovery architecture. It is not one.
No conflict resolution exists, as covered above. No global ordering exists across regions. And critically, there is no protection from logical corruption: a poison record, a buggy producer, or a mistaken topic deletion propagates to every cluster at replication speed. Bidirectional replication makes this worse than single-cluster operation — the blast radius is every region, and it arrives in seconds.
Replication is not backup. Two live clusters give you two copies of your current state, including every mistake in it. Neither cluster can answer "what did this topic contain yesterday at 14:00?" — the question every corruption incident eventually asks. That takes an independent, point-in-time recovery path outside both clusters.
Add an independent recovery point
Run a continuous backup per cluster, each writing to object storage. Excluding the remote-prefixed topics keeps each backup scoped to the data that cluster originated, so the two backups together cover the whole estate exactly once:
mode: backup
backup_id: "east-active-feed"
source:
bootstrap_servers:
- kafka-0.east.svc:9092
- kafka-1.east.svc:9092
topics:
include:
- "orders-*"
- "payments-*"
exclude:
- "west.*"
storage:
backend: s3
bucket: company-kafka-backups
region: eu-west-1
prefix: east
backup:
continuous: true
include_offset_headers: true
consumer_group_snapshot: true
include_offset_headers stores each record's original offset, which enables offset translation at restore time. consumer_group_snapshot writes group positions to storage on each cycle, so a restore can bring consumers back with their offsets — to either cluster of the pair, or to a third cluster entirely:
mode: restore
backup_id: "east-active-feed"
target:
bootstrap_servers:
- kafka-0.recovery.svc:9092
storage:
backend: s3
bucket: company-kafka-backups
region: eu-west-1
prefix: east
restore:
create_topics: true
consumer_group_strategy: header-based
auto_consumer_groups: true
This is the recovery point active-active cannot provide: a validated, point-in-time copy that a corrupted record never reached. The disaster recovery use case shows where this fits in a full DR design, and the configuration reference documents every option used above. For a comparison of MM2's replication role against backup tooling, see the MirrorMaker comparison.
Kafka active-active readiness checklist
- Replication policy chosen, and cycle-safety verified for the topology
- Producers pinned to their local cluster in every failure mode
- Consumers aggregate local + remote-prefixed topics where the full stream matters
- Keys assigned a home region, or divergence explicitly accepted
- Consumers idempotent against cross-cluster duplicates
- Heartbeats and checkpoint lag monitored in both directions
- Consumer group failover tested both directions, replay window measured
- Independent continuous backup running per cluster, remote topics excluded
- Corruption drill run: restore a pre-incident recovery point to a clean cluster
Active-active Kafka is an assembled architecture, not a switch. Teams that succeed with it pin writes locally, watch checkpoints as closely as lag, and keep a recovery point that replication cannot poison.
Start with one topic family. Prove group failover in both directions, run the corruption drill, and only then expand the pattern to the rest of the estate.
Frequently asked questions
Does Kafka replicate messages on each cluster?
Not natively. Kafka replicates partitions across brokers within one cluster only. Copying records between clusters requires an external tool such as MirrorMaker 2, and an active-active topology runs that replication in both directions with cluster-prefixed topic names to prevent loops.
How does Kafka replication work?
Inside a cluster, followers fetch each partition from its leader to maintain the replication factor. Between clusters, a tool like MirrorMaker 2 consumes from one cluster and produces to another, translating consumer offsets through checkpoints. Active-active runs one such flow in each direction.
How to backup Kafka?
Run a continuous backup of each cluster to object storage with offset headers and consumer-group snapshots enabled. In an active-active pair, back up each cluster and exclude the remote-prefixed topics so every record is stored exactly once, then validate restores on a schedule.