Skip to content

Kafka CLI commands for day-2 operations

Kafka
Chad Harris·September 22, 2026·12 min read·Updated

Kafka CLI commands are the shell scripts in the bin/ directory of an Apache Kafka distribution, such as kafka-topics.sh, kafka-consumer-groups.sh and kafka-configs.sh. On Kafka 4.x the admin tools connect with --bootstrap-server host:port, and none accepts --zookeeper.

This page groups the commands operators run on production clusters by job: topics, console produce and consume, consumer groups and offset resets, configs and cluster health, and the secured, multi-cluster version of each. Every command and flag was checked against the Apache Kafka 4.3 documentation and the option definitions in the 4.3.1 tool sources, with the kcat or kafkactl equivalent where it saves time. The complete Kafka guide covers what each of these objects is.

Before you start: the bin directory and --bootstrap-server

Download a binary release from Apache Kafka, extract it, and run the scripts from its bin/ directory. They need a Java runtime and network access to a broker, and nothing else. The Kafka 4.3 quickstart and the basic operations guide are the reference, and each tool prints every option it accepts when you run it with no arguments.

Every admin tool takes a comma-separated list of brokers:

bin/kafka-topics.sh --bootstrap-server broker1:9092,broker2:9092 --list

Three Kafka 4.x changes break commands copied from older posts:

  • ZooKeeper mode was removed in Kafka 4.0, so there is no --zookeeper flag on any tool. The admin tools connect through --bootstrap-server, and tools that manage controller-side state, such as kafka-configs.sh, kafka-acls.sh, kafka-features.sh and kafka-metadata-quorum.sh, also accept --bootstrap-controller.
  • Since 4.0, --bootstrap-server only accepts comma-separated values. A space-separated list throws an exception.
  • Kafka 4.2 renamed several options for consistency (KIP-1147). The old names still work until Kafka 5.0 but print a deprecation warning. The Kafka 4.3 upgrade notes list them all, and the ones you meet daily are these:
Old option Tool Kafka 4.2+ option
--consumer.config, --producer.config Console consumer and producer --command-config
--consumer-property, --producer-property Console consumer and producer --command-property
--property Console consumer --formatter-property
--property Console producer --reader-property
--admin.config kafka-leader-election.sh --command-config

--command-config is the flag that matters most in production, because it is how every tool picks up credentials. The secured version of each command is covered below.

Topic management: create, list, describe, delete

List every topic the principal can describe, and hide internal topics such as __consumer_offsets:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --list --exclude-internal

Describe one topic to see its partition count, replication factor, leaders, replicas, in-sync replicas and any config overrides:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic orders

Create a topic with an explicit partition count, replication factor and retention. --if-not-exists makes the command safe to rerun from a script:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic orders \
  --partitions 12 --replication-factor 3 --config retention.ms=604800000 --if-not-exists

Add partitions with --alter. Kafka cannot reduce a topic’s partition count, and adding partitions changes which partition a key hashes to, so on a keyed topic read Kafka topic vs partition before you run it:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --alter --topic orders --partitions 24

Delete a topic:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic orders

The describe filters are the fastest health check on a cluster. Run them without --topic to scan every topic:

# partitions with fewer in-sync replicas than replicas
bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --under-replicated-partitions

# partitions below min.insync.replicas, which reject acks=all writes
bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --under-min-isr-partitions

# partitions with no available leader
bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --unavailable-partitions

The same jobs in kcat and kafkactl. kcat’s -L lists metadata for every topic, and -J returns it as JSON:

kcat -b localhost:9092 -L -J | jq .
kafkactl get topics
kafkactl describe topic orders

The Kafka topics page is the operational reference for the topic itself.

Producing and consuming from the console

The console producer reads lines from stdin and writes each one as a record. To send keyed records, tell its reader to split each line on a separator:

bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic orders \
  --reader-property parse.key=true --reader-property key.separator=:

Type order-123:{"status":"paid"} and the record lands with key order-123.

The console consumer prints records to stdout. For inspection, read from the beginning, stop after a fixed number of records, and print the key, partition, offset and timestamp beside each value:

bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders \
  --from-beginning --max-messages 10 \
  --formatter-property print.key=true --formatter-property print.partition=true \
  --formatter-property print.offset=true --formatter-property print.timestamp=true

Without --group, the console consumer uses a throwaway group. Never pass --group with a production application’s group ID when you only want to look. The console consumer joins that group, triggers a rebalance of the real consumers, and commits its own offsets.

To see how far a topic has grown without reading it, kafka-get-offsets.sh prints the end offset of each partition, or the earliest with --time earliest:

bin/kafka-get-offsets.sh --bootstrap-server localhost:9092 --topic orders
bin/kafka-get-offsets.sh --bootstrap-server localhost:9092 --topic orders --time earliest

kcat does the same jobs with less typing. Read the last 10 records of partition 0 and exit, wrap consumed records in JSON, or produce from stdin:

kcat -C -b localhost:9092 -t orders -p 0 -o -10 -e
kcat -C -b localhost:9092 -t orders -J -e
kcat -P -b localhost:9092 -t orders

To search and filter records by field value rather than read them in order, how to inspect Kafka topics, messages and consumer groups from the terminal pairs kcat with jq.

Consumer groups: describe, check lag, reset offsets

List groups. kafka-groups.sh lists every type of group, including share groups and Streams groups, while kafka-consumer-groups.sh lists consumer groups and can filter by state:

bin/kafka-groups.sh --bootstrap-server localhost:9092 --list
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list --state stable,empty

Check lag. --describe prints CURRENT-OFFSET, LOG-END-OFFSET and LAG for every partition the group has committed offsets for, and it works when every consumer in the group is down:

bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group orders-app

Read lag per partition, not as a group total. One stuck partition with a climbing LAG while its siblings sit near zero is the shape of a poison pill or a hot key, and how to find and skip a poison pill covers confirming it. Two more views explain a lagging group:

# which member owns which partitions
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group orders-app --members --verbose

# coordinator, assignment strategy, state and member count
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group orders-app --state

Reset offsets with care. In my talk on Kafka operational incidents, my advice was to “use latest in production and make offset resets a deliberate, manual operation”. The tool is built for that. A reset only works while the group has no active members, so stop the consumers first. Without --execute the command is a dry run that prints the new offset for each partition:

bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --reset-offsets \
  --group orders-app --topic orders --to-datetime 2026-09-22T09:00:00.000

Read the plan, then run the identical command with --execute:

bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --reset-offsets \
  --group orders-app --topic orders --to-datetime 2026-09-22T09:00:00.000 --execute

The other reset targets are --to-earliest, --to-latest, --to-offset, --shift-by, --by-duration (for example PT30M), --to-current and --from-file. Limit the scope to specific partitions with --topic orders:0,1,2, and save the plan as CSV with --export so the reset is on record. kafkactl follows the same preview-then-execute pattern:

kafkactl describe consumer-group orders-app --only-with-lag
kafkactl reset offset orders-app --topic orders --to-datetime 2026-09-22T09:00:00.000Z
kafkactl reset offset orders-app --topic orders --to-datetime 2026-09-22T09:00:00.000Z --execute

The full group cheat sheet is on the Kafka consumer groups page, and the best tools to reset consumer group offsets compares the options for doing it at scale.

Configs and cluster health: brokers and topics

kafka-configs.sh reads and changes dynamic configs for topics, brokers, users, clients, groups and IPs. Describe a topic’s overrides, or add --all to include every config and its default:

bin/kafka-configs.sh --bootstrap-server localhost:9092 --describe --entity-type topics --entity-name orders
bin/kafka-configs.sh --bootstrap-server localhost:9092 --describe --entity-type topics --entity-name orders --all

Change or remove a topic config:

bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter --entity-type topics --entity-name orders \
  --add-config retention.ms=259200000
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter --entity-type topics --entity-name orders \
  --delete-config retention.ms

Broker configs work the same way with --entity-type brokers. Use a broker ID for one broker, or --entity-default for the cluster-wide dynamic default:

bin/kafka-configs.sh --bootstrap-server localhost:9092 --describe --entity-type brokers --entity-name 1 --all
bin/kafka-configs.sh --bootstrap-server localhost:9092 --describe --entity-type brokers --entity-default

To find every topic that differs from the broker defaults, ask kafka-topics.sh:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topics-with-overrides

Run the broker describe on a schedule, not only when something breaks. As I said in the incidents talk, config changes belong in version control and CI where you can manage it, but “some environments keep a break-glass option for emergency config changes that never quite makes it back into GitOps.” Diffing the running config against your baseline is how you find those changes before they surprise the next person. The best tools to manage Kafka broker configs covers doing that across a fleet.

Three commands answer “is the cluster itself healthy” on KRaft. The metadata quorum summary shows the active controller, the high watermark and how far each voter lags:

bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --replication

Check which API versions each broker supports, which is useful when a client and a broker disagree after an upgrade:

bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092

See disk usage per partition on each broker’s log directories. This is one of the few bundled tools that prints JSON:

bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 --describe --broker-list 1,2,3 --topic-list orders

Kafka KRaft explains the quorum those commands report on. KRaft became available in Kafka 3.3 and reached full feature parity with ZooKeeper in 3.9.

Running the same tasks against a secured, multi-cluster production topology

A production cluster needs credentials, and every tool reads them from a client properties file passed with --command-config. For SASL/SCRAM over TLS, the file looks like this, following the Kafka SASL documentation:

security.protocol=SASL_SSL
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="ops-admin" password="change-me";

Add a truststore if your brokers use a private certificate authority. For OAuth, the mechanism is OAUTHBEARER with its own login module and token endpoint settings. Pass the file on every call:

bin/kafka-consumer-groups.sh --bootstrap-server prod-broker1:9093 --command-config prod.properties \
  --describe --group orders-app

With several clusters, keep one properties file per cluster, readable only by you, and put the cluster name in the file name. The scripts have no notion of a named context, so the file name and the bootstrap address in your command are the only things telling you which cluster you are about to change. kafkactl contexts and kcl profiles store the same settings under a name, which is why many operators move to one of them once they manage more than two clusters.

ACLs decide what each principal can do. List the ACLs for a principal before you debug an authorization error:

bin/kafka-acls.sh --bootstrap-server prod-broker1:9093 --command-config prod.properties \
  --list --principal User:orders-app

Grant a service the ACLs a consumer needs (Read and Describe on the topic, Read on the group) with the --consumer shortcut, or a producer’s with --producer:

bin/kafka-acls.sh --bootstrap-server prod-broker1:9093 --command-config prod.properties \
  --add --allow-principal User:orders-app --consumer --topic orders --group orders-app

--remove asks for confirmation before it deletes ACLs, unless you pass --force. Keep that prompt for anything run by hand. Kafka’s authorization documentation covers the full option list, and it is explicit that a resource with no matching ACL is restricted to super users. The Kafka ACL page walks through designing them, and Kafka security architecture for production covers the listener and authentication side.

One principal in a properties file is also the limit of what the scripts can control. Whoever holds prod.properties can do everything that principal can, and nothing records which person ran which command.

The governed equivalent: the Kpow CLI

The Kpow CLI, fh, runs most of the same jobs through the Kpow API instead of a Kafka principal. It signs in through the Kpow deployment’s single sign-on, so each command runs as the person who ran it, under the RBAC and tenancy rules the Kpow web UI already enforces. I showed it working in the CLI, terminal UI and agentic skills demo. Kpow is Factor House’s product, and I work at Factor House.

Install it, set up a named context for the deployment, and sign in once. On a deployment with single sign-on, fh auth login opens the browser for the authorization code flow, and reuses a refresh token on later runs instead:

brew install factorhouse/tap/fh
fh config configure
fh auth login

The day-2 commands from this page then look like this. --output json works on every command, which is the form scripts and AI agents should use:

fh kafka topic list --output json
fh kafka topic get --topic orders
fh kafka group get --group-id orders-app --output json
fh kafka topic config update --topic orders --config-name retention.ms --value 259200000
fh kafka cluster kraft-quorum

Offset resets take explicit positions, one --assignment per partition in the form topic, partition and offset:

fh kafka group reset-offsets --group-id orders-app --assignment orders:0:15000 --assignment orders:1:14870

The same binary opens an interactive view of the same brokers, topics and groups with fh tui, which is the route to take when you are looking around rather than scripting. The best Kafka terminal UIs compares it with the open-source k9s-style options.

The difference from the scripts is where the permission check happens. Kpow’s RBAC policies can allow, deny or stage each action for each role. When a policy stages a write, fh reports “Mutation staged for admin approval” and an administrator approves or rejects it in Kpow. The same rules apply whether the command came from a person or from an AI agent using the CLI. Setting up an agent’s identity and approval step on any Kafka, with or without Kpow, is covered in how to let an AI agent operate Kafka safely.

Two limits are worth knowing before you script against it: the CLI does not produce or consume messages, and it has no ACL commands, so the console tools and kafka-acls.sh above still do those jobs. If you are deciding which CLI to standardise on rather than which command to run, the best Kafka CLI tools scores the bundled scripts, kcat, kafkactl, kcl, the Kpow CLI and others on one rubric.

Kpow live demo

See the same jobs run through Kpow

Watch the Kpow CLI inspect brokers and topic assignments, return JSON for scripts, and sign in through the single sign-on your Kpow deployment already uses.

Built for platform engineers who run Kafka from a terminal.

Watch the demo

FAQ

How can I list all topics in Kafka?

Run bin/kafka-topics.sh --bootstrap-server localhost:9092 --list. Add --exclude-internal to hide internal topics, and --command-config client.properties on a secured cluster. The result only includes topics your principal is allowed to describe.

How can I describe topics in Kafka?

Run bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic orders for one topic, or omit --topic to describe all of them. The output shows partitions, leaders, replicas, in-sync replicas and config overrides, and --under-replicated-partitions narrows it to partitions that need attention.

How do I connect to a Kafka cluster from the command line?

Pass the brokers with --bootstrap-server host1:9092,host2:9092, and on a secured cluster add --command-config with a properties file holding security.protocol, sasl.mechanism and the credentials. kafka-broker-api-versions.sh --bootstrap-server is a quick way to confirm the connection works.

How can I run Kafka locally?

Download Kafka 4.x, format the storage directory with kafka-storage.sh, and start a single KRaft node with kafka-server-start.sh, as the Kafka quickstart shows. Kafka in Docker covers the container route, and Factor House Local gives you a local stack with Kpow included.

Related reading