3.1 Dynamic Configuration & Runtime Changes
Module Overview
Goal: Teach safe runtime changes to Kafka configuration.
What You'll Learn:
- Static vs dynamic configurations
- Broker-level dynamic configs
- Topic-level configuration overrides
- Client quotas via dynamic configuration
- When changes require restarts
- Safe operational practices for config changes
What's NOT Covered Here:
- Performance tuning strategy (covered in Module 5.1)
- Monitoring metrics (covered in Module 4.x)
- Specific configuration values and tuning (covered throughout other modules)
Key Insight: Dynamic configuration enables zero-downtime changes for many settings, but understanding which configs are dynamic vs static is critical.
1. Static vs Dynamic Configuration
Configuration Types
STATIC CONFIGS:
├─ Set in server.properties file
├─ Require broker restart to take effect
├─ Example: broker.id, log.dirs, listeners
└─ Cannot be changed at runtime
DYNAMIC CONFIGS:
├─ Can be changed at runtime
├─ No broker restart needed
├─ Take effect immediately or after brief delay
└─ Stored in ZooKeeper or KRaft metadata
Dynamic vs Static Broker Configurations
Complete Reference Table:
┌──────────────────────────────┬──────────┬─────────────────────────────┐
│ Configuration │ Type │ Description │
├──────────────────────────────┼──────────┼─────────────────────────────┤
│ DYNAMIC (No Restart Required) │
├──────────────────────────────┼──────────┼─────────────────────────────┤
│ log.retention.ms │ Dynamic │ Time-based retention │
│ log.retention.bytes │ Dynamic │ Size-based retention │
│ log.segment.bytes │ Dynamic │ Segment file size │
│ log.cleaner.threads │ Dynamic │ Log compaction threads │
│ log.cleaner.min.compaction │ Dynamic │ Compaction dirty ratio │
│ .lag │ │ │
│ min.insync.replicas │ Dynamic │ Minimum ISR size │
│ max.connections │ Dynamic │ Max client connections │
│ max.connections.per.ip │ Dynamic │ Per-IP connection limit │
│ num.network.threads │ Dynamic* │ Network request threads │
│ num.io.threads │ Dynamic* │ Disk I/O threads │
│ background.threads │ Dynamic │ Background task threads │
│ compression.type │ Dynamic │ Broker-side compression │
│ leader.replication.throttled │ Dynamic │ Replication throttling │
│ .rate │ │ │
│ follower.replication │ Dynamic │ Follower fetch throttling │
│ .throttled.rate │ │ │
│ unclean.leader.election │ Dynamic │ Allow unclean elections │
│ .enable │ │ │
│ log.flush.interval.messages │ Dynamic │ Flush frequency │
│ log.flush.interval.ms │ Dynamic │ Flush timeout │
└──────────────────────────────┴──────────┴─────────────────────────────┘
┌──────────────────────────────┬──────────┬─────────────────────────────┐
│ STATIC (Restart Required) │
├──────────────────────────────┼──────────┼─────────────────────────────┤
│ broker.id │ Static │ Unique broker identifier │
│ log.dirs │ Static │ Data directory paths │
│ zookeeper.connect │ Static │ ZooKeeper connection string │
│ controller.quorum.voters │ Static │ KRaft controller quorum │
│ listeners │ Static │ Network listeners │
│ advertised.listeners │ Static │ Advertised endpoints │
│ inter.broker.listener.name │ Static │ Inter-broker communication │
│ security.inter.broker │ Static │ Inter-broker security │
│ .protocol │ │ │
│ num.partitions │ Static │ Default partition count │
│ default.replication.factor │ Static │ Default replication factor │
│ auto.create.topics.enable │ Static │ Auto-create topics │
│ delete.topic.enable │ Static │ Allow topic deletion │
│ offsets.topic.replication │ Static │ Offsets topic replication │
│ .factor │ │ │
│ transaction.state.log │ Static │ Transaction log replication │
│ .replication.factor │ │ │
└──────────────────────────────┴──────────┴─────────────────────────────┘
* Some configs like num.network.threads and num.io.threads may be dynamically
adjustable in recent Kafka versions, but changes are disruptive. Check your version.
How to Check if a Config is Dynamic:
# View all configs with their update modes
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--describe --all
# Look for "updateMode" field:
# - READ_ONLY: Static (restart required)
# - PER_BROKER: Dynamic per-broker
# - CLUSTER_WIDE: Dynamic cluster-wide
Examples:
# DYNAMIC: Can be changed at runtime
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers --entity-name 0 --alter \
--add-config log.retention.ms=86400000
# Broker applies immediately, no restart needed ✓
# STATIC: Requires restart
# To change log.dirs, must edit server.properties:
log.dirs=/data1/kafka,/data2/kafka,/data3/kafka
# Then restart broker for change to take effect
systemctl restart kafka
How Dynamic Configs Work
LIFECYCLE OF DYNAMIC CONFIG CHANGE:
1. Admin issues config change
└─ via kafka-configs.sh or Admin API
2. Config stored in coordination system
└─ ZooKeeper: /config/... znodes
└─ KRaft: __cluster_metadata topic
3. Brokers watch for config changes
└─ Receive notification of update
4. Brokers apply new config
└─ Update in-memory configuration
5. New behavior takes effect
└─ Immediately for most configs
2. Configuration Scopes
Three Levels of Configuration
1. CLUSTER-DEFAULT (Broker-level defaults)
└─ Applies to all brokers unless overridden
2. PER-BROKER (Broker-specific)
└─ Applies to specific broker only
3. PER-TOPIC (Topic-level override)
└─ Applies to specific topic only
Precedence:
Per-Topic > Per-Broker > Cluster-Default > server.properties
Broker vs Topic Configuration Scope
Understanding which scope applies where:
┌────────────────┬────────────────────────────────┬─────────────────────────────┐
│ Scope │ Applies To │ Changed Via │
├────────────────┼────────────────────────────────┼─────────────────────────────┤
│ Broker-Level │ Global defaults for all topics │ server.properties OR │
│ (Cluster-Wide) │ Broker-specific settings │ kafka-configs.sh │
│ │ │ --entity-type brokers │
│ │ │ │
│ Examples: │ log.retention.hours=168 │ Affects all topics unless │
│ │ num.io.threads=8 │ overridden │
│ │ max.connections=1000 │ │
├────────────────┼────────────────────────────────┼─────────────────────────────┤
│ Topic-Level │ Per-topic overrides │ kafka-configs.sh │
│ │ Specific topic behavior │ --entity-type topics │
│ │ │ OR Admin API │
│ │ │ │
│ Examples: │ retention.ms=86400000 │ Overrides broker default │
│ │ cleanup.policy=compact │ for THIS topic only │
│ │ min.insync.replicas=2 │ │
└────────────────┴────────────────────────────────┴─────────────────────────────┘
PRECEDENCE RULE:
└─ Topic-level configuration ALWAYS overrides broker-level default
Real-World Example:
# Broker Default (server.properties)
log.retention.hours=168 # 7 days for all topics
# Result:
├─ Topic "orders": 7 days (uses broker default)
├─ Topic "analytics": 7 days (uses broker default)
└─ Topic "user-activity": 7 days (uses broker default)
# Now apply topic-level override
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name user-activity \
--alter \
--add-config retention.ms=86400000 # 1 day
# New Result:
├─ Topic "orders": 7 days (broker default)
├─ Topic "analytics": 7 days (broker default)
└─ Topic "user-activity": 1 day (OVERRIDE wins!)
# Topic-level config takes precedence ✓
When to Use Each Scope:
USE BROKER-LEVEL WHEN:
├─ Setting defaults for most/all topics
├─ Configuring broker behavior (threads, connections)
├─ Resource management (memory, CPU)
└─ Want simple, consistent configuration
USE TOPIC-LEVEL WHEN:
├─ Topic needs different retention than default
├─ Topic requires special cleanup policy (compaction)
├─ Topic has unique durability requirements
├─ Compliance requires topic-specific settings
└─ Testing configuration before cluster-wide change
Viewing Configuration Precedence:
# View topic config and its source
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name user-activity \
--describe --all
# Output shows source of each config:
# retention.ms=86400000 sensitive=false
# source=DYNAMIC_TOPIC_CONFIG ← Topic override
#
# compression.type=producer sensitive=false
# source=DEFAULT_CONFIG ← Using broker default
3. Viewing Configuration
View Broker Configuration
# View all configs for broker
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--describe
# View cluster-wide defaults
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-default \
--describe
# Show only dynamic configs
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--describe | grep DYNAMIC
View Topic Configuration
# View topic configs
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--describe
# View all configs (including defaults)
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--describe --all
View Client Quota Configuration
# View user quotas
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type users \
--entity-name alice \
--describe
# View client quotas
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type clients \
--entity-name my-producer \
--describe
4. Broker-Level Dynamic Configs
Common Dynamic Broker Configs
COMMONLY CHANGED DYNAMIC CONFIGS:
1. Logging Levels
└─ kafka.log.level
2. Replication Throttling
└─ leader.replication.throttled.rate
└─ follower.replication.throttled.rate
3. Connection Limits
└─ max.connections
└─ max.connections.per.ip
4. SSL/TLS Settings
└─ ssl.keystore.location
└─ ssl.truststore.location
5. Metrics Collection
└─ metric.reporters
6. Background Thread Counts
└─ num.recovery.threads.per.data.dir
└─ num.replica.fetchers
Changing Broker Config
# Set config for specific broker
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--add-config max.connections=2000
# Set cluster-wide default
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-default \
--alter \
--add-config max.connections=2000
# Remove config (revert to static value)
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--delete-config max.connections
Example: Change Log Level
# Increase log level for debugging
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--add-config kafka.server.KafkaApis=DEBUG
# Revert to default (INFO)
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--delete-config kafka.server.KafkaApis
5. Topic-Level Dynamic Configs
Common Dynamic Topic Configs
ALL TOPIC CONFIGS ARE DYNAMIC:
1. Retention
└─ retention.ms
└─ retention.bytes
2. Cleanup
└─ cleanup.policy
└─ min.cleanable.dirty.ratio
└─ segment.ms
3. Replication
└─ min.insync.replicas
└─ unclean.leader.election.enable
4. Compression
└─ compression.type
5. Segment
└─ segment.bytes
└─ segment.ms
6. Message Limits
└─ max.message.bytes
Changing Topic Config
# Change retention to 14 days
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--alter \
--add-config retention.ms=1209600000
# Change multiple configs at once
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--alter \
--add-config retention.ms=1209600000,compression.type=lz4,min.insync.replicas=2
# Remove config (revert to broker default)
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--alter \
--delete-config retention.ms
6. Client Quotas (Dynamic Configuration)
Quota Types
CLIENT QUOTAS CONTROL:
1. Producer Quota
└─ Bytes produced per second
2. Consumer Quota
└─ Bytes consumed per second
3. Request Quota
└─ Request percentage (CPU time)
Applied to:
├─ User
├─ Client ID
└─ User + Client ID combination
Setting Client Quotas
# Set producer quota for user
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type users \
--entity-name alice \
--alter \
--add-config producer_byte_rate=1048576,consumer_byte_rate=2097152
# Set quota for client ID
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type clients \
--entity-name my-producer \
--alter \
--add-config producer_byte_rate=10485760
# Set quota for user + client combination
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type users \
--entity-name alice \
--entity-type clients \
--entity-name my-app \
--alter \
--add-config producer_byte_rate=2097152
# Set default user quota
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type users \
--entity-default \
--alter \
--add-config producer_byte_rate=1048576,consumer_byte_rate=2097152
More details in Module 6.1: Quotas & Resource Management.
7. When Restarts Are Required
Configs Requiring Restart
STATIC CONFIGS (MUST RESTART):
BROKER IDENTITY:
├─ broker.id
├─ broker.rack
└─ reserved.broker.max.id
NETWORKING:
├─ listeners
├─ advertised.listeners
└─ listener.security.protocol.map
STORAGE:
├─ log.dirs
└─ num.io.threads
COORDINATION:
├─ zookeeper.connect (ZK mode)
└─ controller.quorum.voters (KRaft mode)
REPLICATION:
├─ num.replica.fetchers
└─ replica.fetch.max.bytes
THREADING:
└─ num.network.threads
Determining If Restart Needed
RULES OF THUMB:
NO RESTART NEEDED:
├─ Topic-level configs (always dynamic)
├─ Client quotas (always dynamic)
├─ Logging levels (always dynamic)
└─ Throttling limits (always dynamic)
RESTART NEEDED:
├─ Network configuration (listeners, ports)
├─ Storage paths (log.dirs)
├─ Thread pool sizes (num.network.threads)
├─ Broker identity (broker.id, rack)
└─ Coordination settings (ZK connect, KRaft voters)
WHEN IN DOUBT:
└─ Check Kafka documentation for specific config
└─ Test in non-production first
8. Safe Configuration Change Practices
Pre-Change Checklist
BEFORE CHANGING CONFIG:
☑ Document current value
☑ Understand impact of change
☑ Test in non-production first
☑ Have rollback plan
☑ Check if restart required
☑ Schedule maintenance window (if restart needed)
☑ Notify stakeholders
☑ Monitor metrics before change (baseline)
Change Process
STEP-BY-STEP:
1. Verify current config
└─ kafka-configs.sh --describe
2. Apply change
└─ kafka-configs.sh --alter --add-config
3. Verify change applied
└─ kafka-configs.sh --describe (check new value)
4. Monitor impact
├─ Check metrics for expected changes
├─ Watch for errors in logs
└─ Validate client behavior
5. Rollback if needed
└─ kafka-configs.sh --alter --delete-config
6. Document change
└─ Update runbooks, wiki, change log
Rollback Strategy
IF CHANGE CAUSES ISSUES:
1. Immediate rollback
└─ Delete config to revert to previous value
└─ Or set explicitly to old value
2. Monitor recovery
└─ Watch metrics return to normal
3. Investigate root cause
└─ Why did change cause issues?
4. Plan alternative approach
└─ Different config value
└─ Phased rollout
9. Common Dynamic Configuration Scenarios
Scenario 1: Increase Retention for Topic
GOAL: Extend data retention from 7 to 14 days
STEPS:
1. Check current retention
kafka-topics.sh --describe --topic orders
2. Change retention
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--alter \
--add-config retention.ms=1209600000
3. Verify
kafka-topics.sh --describe --topic orders
IMPACT:
├─ Immediate: No impact (data not deleted yet)
├─ Over time: Disk usage increases
└─ Monitoring: Watch disk usage trends
Scenario 2: Throttle Replication During Reassignment
GOAL: Limit replication bandwidth to 50 MB/s during partition reassignment
STEPS:
1. Set throttle on all brokers
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--add-config leader.replication.throttled.rate=52428800,follower.replication.throttled.rate=52428800
# Repeat for all brokers
2. Execute reassignment
kafka-reassign-partitions.sh --execute
3. Monitor progress
kafka-reassign-partitions.sh --verify
4. Remove throttle after completion
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--delete-config leader.replication.throttled.rate,follower.replication.throttled.rate
IMPACT:
├─ Slower reassignment
├─ Less impact on production traffic
└─ Better control over resource usage
Scenario 3: Enable Debug Logging
GOAL: Debug connectivity issues for specific broker
STEPS:
1. Enable DEBUG logging
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--add-config kafka.network.SocketServer=DEBUG
2. Reproduce issue while capturing logs
3. Revert to INFO level
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type brokers \
--entity-name 0 \
--alter \
--delete-config kafka.network.SocketServer
IMPACT:
├─ Increased log volume
├─ Potential performance impact
└─ Should be temporary (minutes, not hours)
Scenario 4: Change min.insync.replicas
GOAL: Increase durability guarantee from 1 to 2
STEPS:
1. Check current ISR size
kafka-topics.sh --describe --topic orders
2. Change min.insync.replicas
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name orders \
--alter \
--add-config min.insync.replicas=2
3. Verify
kafka-topics.sh --describe --topic orders
IMPACT:
├─ Higher durability (leader + 1 follower must ack)
├─ Lower availability (writes fail if ISR < 2)
├─ Producers using acks=all will experience change
└─ Ensure replication factor ≥ 2!
10. Bulk Configuration Changes
Applying Configs to Multiple Topics
# Get all topics
kafka-topics.sh --bootstrap-server localhost:9092 --list > topics.txt
# Apply config to each topic
while read topic; do
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name "$topic" \
--alter \
--add-config retention.ms=604800000
done < topics.txt
Using Java Admin API (Programmatic Configuration)
For automation and integration, use the Admin API to manage configurations programmatically.
Setup Admin Client
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.common.config.ConfigResource;
import java.util.*;
import java.util.concurrent.ExecutionException;
public class DynamicConfigManager {
private AdminClient adminClient;
public void init(String bootstrapServers) {
Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 30000);
adminClient = AdminClient.create(props);
}
public void close() {
adminClient.close();
}
}
Example 1: Alter Topic Configuration
public void setTopicRetention(String topicName, long retentionMs)
throws ExecutionException, InterruptedException {
// Define the topic resource
ConfigResource resource = new ConfigResource(
ConfigResource.Type.TOPIC,
topicName
);
// Create config entry
ConfigEntry retentionConfig = new ConfigEntry(
"retention.ms",
String.valueOf(retentionMs)
);
// Create alter operation
AlterConfigOp op = new AlterConfigOp(
retentionConfig,
AlterConfigOp.OpType.SET
);
// Apply the configuration
Map<ConfigResource, Collection<AlterConfigOp>> configs =
Collections.singletonMap(resource, Collections.singleton(op));
adminClient.incrementalAlterConfigs(configs).all().get();
System.out.println("✓ Updated retention for topic: " + topicName);
}
// Usage
configManager.setTopicRetention("orders", 86400000L); // 1 day
Example 2: View Topic Configuration
public void describeTopicConfig(String topicName)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(
ConfigResource.Type.TOPIC,
topicName
);
DescribeConfigsResult result = adminClient.describeConfigs(
Collections.singleton(resource)
);
Map<ConfigResource, Config> configs = result.all().get();
Config topicConfig = configs.get(resource);
System.out.println("Configuration for topic: " + topicName);
System.out.println("─".repeat(50));
for (ConfigEntry entry : topicConfig.entries()) {
System.out.printf("%-30s = %s%n", entry.name(), entry.value());
// Show if it's a dynamic override or default
if (entry.source() == ConfigEntry.ConfigSource.DYNAMIC_TOPIC_CONFIG) {
System.out.println(" (dynamic override)");
}
}
}
Example 3: Delete Config Override (Revert to Default)
public void deleteTopicConfigOverride(String topicName, String configKey)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(
ConfigResource.Type.TOPIC,
topicName
);
// Create DELETE operation
AlterConfigOp deleteOp = new AlterConfigOp(
new ConfigEntry(configKey, null), // value=null for delete
AlterConfigOp.OpType.DELETE
);
Map<ConfigResource, Collection<AlterConfigOp>> configs =
Collections.singletonMap(resource, Collections.singleton(deleteOp));
adminClient.incrementalAlterConfigs(configs).all().get();
System.out.println("✓ Deleted override for: " + configKey);
System.out.println(" Topic will now use broker default");
}
// Usage
configManager.deleteTopicConfigOverride("orders", "retention.ms");
Example 4: Set Multiple Configs at Once
public void configureHighThroughputTopic(String topicName)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(
ConfigResource.Type.TOPIC,
topicName
);
// Define multiple configurations
List<AlterConfigOp> ops = Arrays.asList(
new AlterConfigOp(
new ConfigEntry("compression.type", "lz4"),
AlterConfigOp.OpType.SET
),
new AlterConfigOp(
new ConfigEntry("min.insync.replicas", "2"),
AlterConfigOp.OpType.SET
),
new AlterConfigOp(
new ConfigEntry("retention.ms", "604800000"), // 7 days
AlterConfigOp.OpType.SET
),
new AlterConfigOp(
new ConfigEntry("segment.ms", "3600000"), // 1 hour
AlterConfigOp.OpType.SET
)
);
Map<ConfigResource, Collection<AlterConfigOp>> configs =
Collections.singletonMap(resource, ops);
adminClient.incrementalAlterConfigs(configs).all().get();
System.out.println("✓ Configured topic for high throughput: " + topicName);
}
Example 5: Bulk Config Update (Pattern Matching)
public void setBulkRetention(String topicPrefix, long retentionMs)
throws ExecutionException, InterruptedException {
// Get all topics matching pattern
Set<String> allTopics = adminClient.listTopics().names().get();
Set<String> matchingTopics = allTopics.stream()
.filter(t -> t.startsWith(topicPrefix))
.collect(Collectors.toSet());
System.out.println("Found " + matchingTopics.size() + " topics matching: " + topicPrefix);
// Prepare configs for all matching topics
Map<ConfigResource, Collection<AlterConfigOp>> configs = new HashMap<>();
for (String topic : matchingTopics) {
ConfigResource resource = new ConfigResource(
ConfigResource.Type.TOPIC,
topic
);
AlterConfigOp op = new AlterConfigOp(
new ConfigEntry("retention.ms", String.valueOf(retentionMs)),
AlterConfigOp.OpType.SET
);
configs.put(resource, Collections.singleton(op));
}
// Apply all configs
adminClient.incrementalAlterConfigs(configs).all().get();
System.out.println("✓ Updated retention for " + matchingTopics.size() + " topics");
}
// Usage
configManager.setBulkRetention("app-", 86400000L); // All topics starting with "app-"
Example 6: Set Broker-Level Dynamic Config
public void setBrokerConfig(int brokerId, String configKey, String configValue)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(
ConfigResource.Type.BROKER,
String.valueOf(brokerId)
);
AlterConfigOp op = new AlterConfigOp(
new ConfigEntry(configKey, configValue),
AlterConfigOp.OpType.SET
);
Map<ConfigResource, Collection<AlterConfigOp>> configs =
Collections.singletonMap(resource, Collections.singleton(op));
adminClient.incrementalAlterConfigs(configs).all().get();
System.out.println("✓ Updated broker " + brokerId + " config: " + configKey);
}
// Usage
configManager.setBrokerConfig(0, "log.retention.hours", "168"); // 7 days
Complete Example: Config Manager Class
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.common.config.ConfigResource;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
public class KafkaConfigManager implements AutoCloseable {
private final AdminClient adminClient;
public KafkaConfigManager(String bootstrapServers) {
Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 30000);
this.adminClient = AdminClient.create(props);
}
public void alterTopicConfig(String topic, String key, String value)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(ConfigResource.Type.TOPIC, topic);
AlterConfigOp op = new AlterConfigOp(
new ConfigEntry(key, value),
AlterConfigOp.OpType.SET
);
Map<ConfigResource, Collection<AlterConfigOp>> configs =
Collections.singletonMap(resource, Collections.singleton(op));
adminClient.incrementalAlterConfigs(configs).all().get();
}
public void describeTopicConfig(String topic)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(ConfigResource.Type.TOPIC, topic);
DescribeConfigsResult result = adminClient.describeConfigs(
Collections.singleton(resource)
);
Config config = result.all().get().get(resource);
config.entries().forEach(entry ->
System.out.println(entry.name() + " = " + entry.value())
);
}
public void deleteTopicConfigOverride(String topic, String key)
throws ExecutionException, InterruptedException {
ConfigResource resource = new ConfigResource(ConfigResource.Type.TOPIC, topic);
AlterConfigOp op = new AlterConfigOp(
new ConfigEntry(key, null),
AlterConfigOp.OpType.DELETE
);
Map<ConfigResource, Collection<AlterConfigOp>> configs =
Collections.singletonMap(resource, Collections.singleton(op));
adminClient.incrementalAlterConfigs(configs).all().get();
}
@Override
public void close() {
adminClient.close();
}
// Main method for testing
public static void main(String[] args) {
try (KafkaConfigManager manager = new KafkaConfigManager("localhost:9092")) {
// Set retention to 1 day
manager.alterTopicConfig("orders", "retention.ms", "86400000");
System.out.println("✓ Updated retention");
// View configuration
manager.describeTopicConfig("orders");
// Revert to default
manager.deleteTopicConfigOverride("orders", "retention.ms");
System.out.println("✓ Reverted to default retention");
} catch (Exception e) {
e.printStackTrace();
}
}
}
11. Configuration Best Practices
DO
✅ Test changes in non-production first
✅ Document all configuration changes
✅ Monitor metrics before and after
✅ Use version control for static configs
✅ Have rollback plan ready
✅ Change one config at a time (for troubleshooting)
✅ Use cluster-wide defaults when appropriate
✅ Remove dynamic overrides when no longer needed
DON'T
❌ Change multiple configs simultaneously
❌ Apply untested configs in production
❌ Forget to monitor after changes
❌ Leave debug logging enabled permanently
❌ Use dynamic configs for values that should be static
❌ Ignore warnings in logs after config changes
❌ Change configs during peak traffic (unless necessary)
❌ Apply configs blindly without understanding impact
12. Configuration Management Tools
Infrastructure as Code
RECOMMENDED APPROACHES:
1. Ansible
└─ Manage server.properties via templates
└─ Apply dynamic configs via kafka-configs.sh tasks
2. Terraform with Kafka Provider
└─ Manage topics and configs declaratively
└─ Track changes in version control
3. GitOps
└─ Store configs in Git
└─ CI/CD pipeline applies changes
└─ Audit trail built-in
4. Configuration Management Platform
└─ Confluent Control Center
└─ LinkedIn Cruise Control
└─ Custom tooling
13. Key Takeaways
You've learned:
✅ Dynamic configs can be changed at runtime (no restart)
✅ Static configs require broker restart
✅ Three config scopes: cluster-default, per-broker, per-topic
✅ kafka-configs.sh is the primary tool for dynamic changes
✅ Topic configs are always dynamic
✅ Client quotas can be set dynamically
✅ Rollback is easy (delete config or set to old value)
✅ Test changes in non-production first
✅ Monitor impact of every configuration change
14. Configuration Change Checklist
PRE-CHANGE:
☑ Document current value
☑ Understand impact
☑ Test in non-production
☑ Have rollback plan
☑ Check if restart needed
☑ Schedule window (if restart)
DURING CHANGE:
☑ Apply change
☑ Verify applied
☑ Monitor logs
☑ Check metrics
POST-CHANGE:
☑ Validate expected behavior
☑ Monitor for period (15-60 min)
☑ Document change
☑ Update runbooks
Summary
Safe configuration management:
Dynamic Configuration =
├─ Zero-downtime changes
├─ Immediate effect
├─ Easy rollback
├─ Powerful tool for operations
└─ Requires careful planning
Safe Practice =
├─ Test first
├─ Monitor always
├─ Rollback if needed
├─ Document everything
└─ Change deliberately
Remember: Dynamic configuration is a powerful operational tool, but power requires responsibility. Always test, always monitor, always have a rollback plan.
Next up: Module 4.1 — Core Kafka Metrics — Build a shared vocabulary for observability.