Guide · AWS MSK Replication

MSK Replication for MCP Servers — Cross-Region DR, Replication Factor, and Producer Acks

MSK Replication (generally available since 2024) copies topics from a source MSK cluster to a destination cluster in a different region, enabling active-passive disaster recovery for MCP probe pipelines. Unlike MirrorMaker2, which requires you to run and manage the connector yourself, MSK Replication is a fully managed replication service: you define the source cluster, destination cluster, and which topics to replicate via an MSKReplicationInfo resource — AWS handles the replication process. However, MSK Replication is only available on provisioned MSK clusters; MSK Serverless clusters cannot be a source or destination. For in-region durability, the more important levers are replication factor (how many broker copies exist per partition) and producer acks (how many replicas must acknowledge a write before the producer considers it durable).

TL;DR

Use replication.factor=3 with min.insync.replicas=2 and producer acks=all for durable probe result ingestion. This guarantees that a write is lost only if two of the three brokers fail simultaneously before the write is consumed. For cross-region DR, enable MSK Replication on your provisioned cluster and point a standby consumer group at the destination cluster. Monitor ReplicationByteLag in CloudWatch — an increasing lag means the destination cluster is falling behind the source. On failover, restart the consumer group from the last committed offset on the destination cluster; expect a brief replay of the lag window.

Replication factor and in-sync replicas

The replication factor (RF) is the number of broker copies for each partition. For a 3-broker cluster, RF=3 means all three brokers hold a copy of every partition. The in-sync replica set (ISR) is the subset of replicas that are caught up to within the replica lag threshold — by default, any replica that is more than 30 seconds behind the leader is removed from the ISR.

min.insync.replicas (ISR) controls the minimum number of ISR members required before a producer write with acks=all is acknowledged. With RF=3 and min.insync.replicas=2:

ConfigurationDurabilityAvailability trade-offRecommended for…
RF=1, acks=1Data lost if broker fails before consumeHighest availability (single broker)Development/staging only
RF=3, min.ISR=1, acks=1Data lost if leader fails before followers catch upGood: partition available with 1 live brokerProbe command topics (low value, can miss one)
RF=3, min.ISR=2, acks=allData lost only if 2 of 3 brokers fail simultaneouslyPartition unavailable if <2 brokers liveProbe result topics (durable, must not drop)
RF=3, min.ISR=3, acks=allMaximum durability: all 3 must ackUnavailable if any broker failsAudit/compliance topics only

Producer acks: three settings compared

The acks producer config controls which acknowledgement the broker sends back before the producer considers a send complete:

acks settingWho acknowledgesDurabilityLatency
acks=0Nobody — fire and forgetZero: message lost on any network blipLowest (no round-trip wait)
acks=1Partition leader onlyMessage survives if leader does not fail within the replication windowLow (one network hop)
acks=all (or acks=-1)All ISR members (subject to min.insync.replicas)Message survives unless more than (RF − min.ISR) brokers fail simultaneouslyHigher (waits for all ISR acks)

The interaction with retries: with acks=all, a producer that gets a NotEnoughReplicasException (ISR < min.insync.replicas) will retry if retries > 0. Set retries=Integer.MAX_VALUE or a high value with delivery.timeout.ms to bound the total retry window. For probe result ingestion, a 30-second delivery timeout aligns with the 60-second probe cycle — if the write hasn't succeeded in 30 seconds, the result is dropped and the next probe will re-verify the endpoint status.

// CDK: MSK cluster with RF=3 and min.insync.replicas=2
import { aws_msk as msk } from 'aws-cdk-lib';

const clusterConfig = new msk.ClusterConfigurationProperty({
  // MSK configuration resource (applied to cluster)
  // Set default topic-level min.insync.replicas
  serverProperties: [
    'min.insync.replicas=2',
    'default.replication.factor=3',
    'auto.create.topics.enable=false',  // prevent accidental topic creation
  ].join('\n'),
});

const cluster = new msk.CfnCluster(this, 'ProbeCluster', {
  clusterName: 'alivemcp-probe-us-east-1',
  kafkaVersion: '3.6.0',
  numberOfBrokerNodes: 3,
  brokerNodeGroupInfo: { /* ... */ },
  configurationInfo: {
    arn: clusterConfig.ref,
    revision: 1,
  },
  encryptionInfo: {
    encryptionInTransit: { clientBroker: 'TLS', inCluster: true },
  },
});

MSK Replication: cross-region active-passive setup

MSK Replication copies topics from a source cluster to a destination cluster in a different region. The destination cluster is read-only during normal operations — probe result consumers are directed to the destination cluster only during failover. To configure MSK Replication:

  1. Create a destination MSK cluster in the DR region (eu-west-1 for us-east-1 primary). It does not need the same broker count — a smaller cluster is fine for standby.
  2. Create an MSK Replicator resource in the source region specifying source cluster ARN, destination cluster ARN, and topic patterns.
  3. Grant the MSK Replication service IAM permissions to read from the source and write to the destination cluster.
// CloudFormation / CDK L1 for MSK Replication
// (No CDK L2 construct as of late 2026 — use CfnReplicator)
import { aws_msk as msk } from 'aws-cdk-lib';

const replicator = new msk.CfnReplicator(this, 'ProbeResultReplicator', {
  replicatorName: 'probe-results-us-to-eu',
  description: 'Replicate probe result topics to EU DR cluster',
  serviceExecutionRoleArn: replicationRole.roleArn,
  kafkaClusters: [
    {
      amazonMskCluster: { mskClusterArn: sourceClusterArn },
      vpcConfig: { subnetIds: sourceSubnetIds, securityGroupIds: [sourceSgId] },
    },
    {
      amazonMskCluster: { mskClusterArn: destClusterArn },
      vpcConfig: { subnetIds: destSubnetIds, securityGroupIds: [destSgId] },
    },
  ],
  replicationInfoList: [{
    sourceKafkaClusterArn: sourceClusterArn,
    targetKafkaClusterArn: destClusterArn,
    targetCompressionType: 'SNAPPY',
    topicReplication: {
      topicsToReplicate: ['probe-results', 'alert-dispatch', 'audit-events'],
      detectAndCopyNewTopics: true,
      copyTopicConfigurations: true,
    },
    consumerGroupReplication: {
      consumerGroupsToReplicate: ['probe-archiver', 'alert-worker'],
      detectAndCopyNewConsumerGroups: true,
      synchroniseConsumerGroupOffsets: true,
    },
  }],
});

With synchroniseConsumerGroupOffsets: true, MSK Replication translates source-cluster offsets to destination-cluster offsets and writes them to the __consumer_offsets topic on the destination. On failover, a consumer pointed at the destination cluster resumes from the translated offset — you do not need to manually reset offsets.

Monitoring replication lag

MSK Replication emits the ReplicationByteLag CloudWatch metric (bytes) per topic partition. A lag value that grows over time indicates the destination is falling behind — the DR cluster would not be a safe failover target until it catches up.

// CDK alarm: alert if replication lag exceeds 50 MB on any partition
import { aws_cloudwatch as cloudwatch, aws_cloudwatch_actions as cw_actions } from 'aws-cdk-lib';

new cloudwatch.Alarm(this, 'ReplicationLagAlarm', {
  alarmName: 'msk-probe-replication-lag',
  metric: new cloudwatch.Metric({
    namespace: 'AWS/MSK/Replicator',
    metricName: 'ReplicationByteLag',
    dimensionsMap: {
      ReplicatorName: 'probe-results-us-to-eu',
    },
    statistic: 'Maximum',
    period: cdk.Duration.minutes(1),
  }),
  threshold: 50 * 1024 * 1024,  // 50 MB
  evaluationPeriods: 3,          // sustained for 3 minutes
  comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
  alarmDescription: 'MSK DR replication falling behind — failover would replay this lag on resume',
});

Common failure modes

SymptomCauseFix
Producer receives NotEnoughReplicasExceptionCluster ISR count dropped below min.insync.replicas (e.g., one broker restarting)Expected during broker rolling restart; configure producer delivery.timeout.ms=30000 to wait for restart to complete. Reduce min.insync.replicas to 1 only for non-critical topics.
UnderReplicatedPartitions metric > 0 for extended periodA follower broker is not catching up — could be network partition or disk pressureInspect broker logs; check ReplicaMaxLag; if a broker is permanently unhealthy, replace the broker node
MSK Replication fails with AccessDeniedReplication IAM role missing permissions on source or destination clusterAdd kafka:DescribeCluster, kafka:GetBootstrapBrokers, and kafka-cluster:* actions for both cluster ARNs to the service execution role
Consumer on destination cluster reads duplicate messages after failoverReplication lag at failover time — destination consumer resumes behind source committed offsetExpected: the consumer will replay the lag window. Make consumer idempotent (upsert by probe run ID) so replays are safe.
MSK Replication only available on provisioned; using MSK ServerlessMSK Replication does not support MSK Serverless clusters as source or destinationImplement a consumer-producer bridge Lambda that reads from the Serverless source cluster and writes to a provisioned destination cluster in the DR region