Before you start
All commands use --bootstrap-server. The old --zookeeper flag has been removed from the topic and config tools and does not exist at all in Kafka 4.x.
The scripts ship in the Kafka bin/ directory as kafka-*.sh. Confluent packages install them without the .sh suffix, for example kafka-topics.
For TLS or SASL clusters, put the client settings in a file and pass it to every command:
# client.properties
security.protocol=SASL_SSL
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="ops" password="...";
export BS=broker1:9092
kafka-topics.sh --bootstrap-server $BS --command-config client.properties --list
The consumer and producer tools take --consumer.config or --producer.config instead of --command-config. Flag names vary slightly between versions, so run any tool with --help to see what your build supports.
Cluster health in 60 seconds
# Can I reach the brokers, and which API versions do they support?
kafka-broker-api-versions.sh --bootstrap-server $BS | grep -E '^[^ ]'
# Partitions in trouble (empty output = good)
kafka-topics.sh --bootstrap-server $BS --describe --under-replicated-partitions
kafka-topics.sh --bootstrap-server $BS --describe --under-min-isr-partitions
kafka-topics.sh --bootstrap-server $BS --describe --unavailable-partitions
# KRaft controller quorum: leader, voters and lag (Kafka 3.3+)
kafka-metadata-quorum.sh --bootstrap-server $BS describe --status
kafka-metadata-quorum.sh --bootstrap-server $BS describe --replication
# Disk use per broker and log directory
kafka-log-dirs.sh --bootstrap-server $BS --describe --topic-list orders
| Output | What it means | Next step |
|---|---|---|
| Under-replicated partitions | A follower is behind or a broker is down | Check broker logs, disk and network on the broker missing from the ISR |
| Under-min-ISR partitions | Producers using acks=all are being rejected | Treat as an incident. Bring the missing replicas back |
| Unavailable partitions | No leader. Reads and writes fail | Restore the broker that holds the replicas |
High Lag in quorum replication | A controller is falling behind | Check that controller's disk and GC |
Topics
kafka-topics.sh --bootstrap-server $BS --list
kafka-topics.sh --bootstrap-server $BS --describe --topic orders
kafka-topics.sh --bootstrap-server $BS --create --topic orders \
--partitions 12 --replication-factor 3 \
--config min.insync.replicas=2 --config retention.ms=604800000
# Add partitions (you can only increase the count)
kafka-topics.sh --bootstrap-server $BS --alter --topic orders --partitions 24
kafka-topics.sh --bootstrap-server $BS --delete --topic orders_old
Warning
Adding partitions changes which partition each key hashes to. Consumers that rely on per-key ordering will see keys move partitions. Agree on a time for the change with the topic's owners first.
Caution
--deleteremoves the topic and its data permanently. There is no undo. Run--describefirst and double-check the topic name, especially if you use tab completion.
Topic and broker configs
# Show the overrides set on a topic
kafka-configs.sh --bootstrap-server $BS --entity-type topics --entity-name orders --describe
# Show every effective value, including defaults
kafka-configs.sh --bootstrap-server $BS --entity-type topics --entity-name orders --describe --all
# Change or remove an override
kafka-configs.sh --bootstrap-server $BS --entity-type topics --entity-name orders \
--alter --add-config retention.ms=259200000,max.message.bytes=2097152
kafka-configs.sh --bootstrap-server $BS --entity-type topics --entity-name orders \
--alter --delete-config retention.ms
# Dynamic broker config (broker id 1, or --entity-default for all brokers)
kafka-configs.sh --bootstrap-server $BS --entity-type brokers --entity-name 1 --describe
Topic config changes take effect without a restart. Lowering retention.ms deletes old segments on the next retention check, so it doubles as an emergency way to free disk.
Consumer groups
kafka-consumer-groups.sh --bootstrap-server $BS --list
# Lag per partition: CURRENT-OFFSET, LOG-END-OFFSET, LAG, CONSUMER-ID
kafka-consumer-groups.sh --bootstrap-server $BS --describe --group orders-app
# Who is in the group, and which partitions each member owns
kafka-consumer-groups.sh --bootstrap-server $BS --describe --group orders-app --members --verbose
# Group state: Stable, PreparingRebalance, Empty, Dead
kafka-consumer-groups.sh --bootstrap-server $BS --describe --group orders-app --state
Reset offsets
The group must be Empty, so stop every consumer first. Always run with --dry-run, read the output, then repeat the command with --execute.
# Preview: replay from a point in time (ISO-8601, UTC)
kafka-consumer-groups.sh --bootstrap-server $BS --group orders-app --topic orders \
--reset-offsets --to-datetime 2026-10-26T06:00:00.000 --dry-run
# Apply it
kafka-consumer-groups.sh --bootstrap-server $BS --group orders-app --topic orders \
--reset-offsets --to-datetime 2026-10-26T06:00:00.000 --execute
| Option | Use it when |
|---|---|
--to-earliest | Full replay of what is still retained |
--to-latest | Skip the backlog. That data is never processed by this group |
--to-datetime <ts> | Replay from a known-good time |
--shift-by -N | Step back N messages per partition |
--to-offset N | Set an exact offset, usually with --topic orders:3 for a single partition |
--from-file plan.csv | Apply a saved plan, for example one produced by --export during a dry run |
Caution
--to-latestsilently skips unprocessed data, and--to-earliestcan reprocess days of events into downstream systems. Agree on the choice with the application owner, and save the current offsets first with--describe > offsets-before.txt.
# Remove a dead group, or its offsets for one topic
kafka-consumer-groups.sh --bootstrap-server $BS --delete --group old-app
kafka-consumer-groups.sh --bootstrap-server $BS --delete-offsets --group orders-app --topic orders_old
Read and write test messages
# Produce keyed messages (type key:value, then Ctrl+D to finish)
kafka-console-producer.sh --bootstrap-server $BS --topic orders \
--property parse.key=true --property key.separator=:
# Read 10 messages from the start, with keys, timestamps and partitions
kafka-console-consumer.sh --bootstrap-server $BS --topic orders --from-beginning \
--max-messages 10 --property print.key=true \
--property print.timestamp=true --property print.partition=true
# Earliest (-2) and latest (-1) offsets per partition (Kafka 3.0+)
kafka-get-offsets.sh --bootstrap-server $BS --topic orders --time -1
Tip
The console consumer creates a random group unless you pass
--group. Don't pass a production group name, or you will commit offsets for that group and steal its partitions.
Move partitions and rebalance leaders
Use this to spread load onto a new broker or empty a broker before you decommission it.
echo '{"version":1,"topics":[{"topic":"orders"}]}' > topics.json
# 1. Generate a proposal. Save the "Current" section as your rollback plan
kafka-reassign-partitions.sh --bootstrap-server $BS \
--topics-to-move-json-file topics.json --broker-list "1,2,3,4" --generate
# 2. Execute with a replication throttle in bytes/sec
kafka-reassign-partitions.sh --bootstrap-server $BS \
--reassignment-json-file plan.json --execute --throttle 50000000
# 3. Check progress. --verify also removes the throttle once the move is done
kafka-reassign-partitions.sh --bootstrap-server $BS \
--reassignment-json-file plan.json --verify
# Move leadership back to the preferred replicas after restarts
kafka-leader-election.sh --bootstrap-server $BS --election-type preferred --all-topic-partitions
Warning
An unthrottled reassignment of a large topic can saturate broker network and disk, and push producer latency up across the whole cluster. Start with a conservative throttle and run
--verifyuntil it reports completion.
ACLs
kafka-acls.sh --bootstrap-server $BS --command-config client.properties --list --topic orders
kafka-acls.sh --bootstrap-server $BS --command-config client.properties --add \
--allow-principal User:orders-app --operation Read --topic orders --group orders-app
A consumer needs Read on the topic and Read on its group. A missing group ACL shows up as GroupAuthorizationException.
Prevent it next time
- Keep a
client.propertiesfor each cluster in your on-call jump host or runbook repo, so nobody has to rebuild the authentication settings during an incident. - Turn off
auto.create.topics.enablein production and create topics with explicit partition, replication andmin.insync.replicassettings. - Script the health checks from this page and run them on a schedule. Alert when under-replicated, under-min-ISR or unavailable partition output is not empty.
- Record every offset reset and reassignment in the incident ticket, with the dry-run output attached.