Skip to main content

How to Delete a Kafka Topic (and Recover It)

· 14 min read
OSO Engineering
The team behind OSO Kafka Backup

A kafka delete topic request is a how-to with a hard ending. The admin command removes the topic. The cluster then drops its replicas and the committed offsets that pointed at it. There is no undo inside Kafka.

Quick Answer

Delete a topic with bin/kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic <name>. The broker config delete.topic.enable is the switch that blocks it. Kafka 4.3 broker configs default that setting to true. When it is false, the broker rejects the delete. The command does not ask for confirmation, and the cluster cannot undo it.

How do I delete a Kafka topic with kafka-topics.sh?​

The basic operations guide deletes a topic with this command.

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

Replace my_topic_name and point --bootstrap-server at a broker. The tool takes one action flag. --delete is that action. It does not prompt.

Since Kafka 3.0, kafka-topics.sh no longer accepts --zookeeper. KIP-604 removed that flag. The 3.0 upgrade notes say to use --bootstrap-server instead.

Kafka 4.0 is stricter about the value. The 4.0 upgrade notes say --bootstrap-server must be a comma-separated list, such as host1:port1,host2:port2. A space-separated list throws.

Confirm the name is gone after the command returns:

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

A successful delete result is not the same second the name vanishes on every broker. The Admin.deleteTopics javadoc says list and describe can still return the topic for several seconds. Wait, then list again, before you treat a still-visible name as a failed delete.

Why is my Kafka topic not deleting?​

A delete fails for a short list of reasons. Most of them are a broker config, an ACL, or a client that creates the name again.

delete.topic.enable​

delete.topic.enable is a broker config. Kafka 4.3 documents it in one sentence pair. When it is true, the admin client can delete topics. When it is false, the broker explicitly rejects the request. The default is true. The update mode is read-only, so a change needs a process restart.

Kafka 4.1 and 4.2 use that same text. The Kafka 4.0 config page still says a disabled delete "will have no effect." The 4.0 controller does not match that sentence. In ControllerApis.scala, a disabled delete throws InvalidRequestException with the message Topic deletion is disabled. on request versions below 3. Newer requests get TopicDeletionDisabledException. The Admin client javadoc describes the same outcome: an exception that says topic deletion is disabled.

If the command reports that error, the flag is false on the broker that handled the request. Turn it on only when you mean to allow deletes, then restart.

Topics still marked for deletion​

"Marked for deletion" is a ZooKeeper-mode message. The old tool, talking to ZooKeeper, printed Topic <name> is marked for deletion. It listed the name as marked for deletion while that marker existed. It also printed that the call would have no impact if delete.topic.enable was not true. That path is the Kafka 2.4 topic tool. Kafka 3.0 removed --zookeeper from this command. Kafka 4.0 removed ZooKeeper mode entirely.

On Kafka 4, a refused delete is an error, not a stuck marker. A name that lingers for a few seconds matches the Admin client note above. If it is still listed after that, read the error from the command. Do not keep reissuing --delete against a topic the cluster already accepted.

ACLs and authorization​

DELETE_TOPICS (API key 20) requires the Delete operation on the Topic resource. The authorization guide maps that pair. A missing privilege returns TOPIC_AUTHORIZATION_FAILED (error code 29) on the topic resource.

With an authorizer on, a principal that has no matching allow is denied. Super users, set with super.users, skip that check. allow.everyone.if.no.acl.found=true opens any resource that has no ACL. A delete that looked blocked often traces to one of those two settings.

The 4.0 controller also treats Delete on the Cluster resource as enough. If that cluster check passes, it does not consult a topic-level ACL. A topic deny does not override a cluster Delete allow. See the same deleteTopics method linked above.

Internal topics​

The operations guide names internal state topics you should not change by hand. They are __consumer_offsets, __transaction_state, __share_group_state, and __cluster_metadata.

Older ZooKeeper tools refused to mark an internal topic for deletion. Kafka 4's kafka-topics.sh --delete calls Admin.deleteTopics. The 4.0 controller path linked above does not apply that old client-side refusal. Do not delete those names. They hold group offsets, transaction state, share-group state, and the KRaft metadata log.

A client is still producing​

auto.create.topics.enable defaults to true. The broker configs describe it as "Enable auto creation of topic on the server." It is read-only.

After a successful delete, a produce or a metadata request for that name can create it again. The new topic is empty. It uses the broker defaults for partitions and replication. It looks like the delete failed. It did not. The client recreated the name.

Stop that client, or set auto.create.topics.enable=false and restart, before you delete a topic that producers still use.

KRaft and ZooKeeper​

Kafka 4 has one metadata path. The 4.0 upgrade notes say ZooKeeper mode is removed, and a 4.0 broker only runs in KRaft. Delete requests go to the active controller. There is no /admin/delete_topics znode to inspect.

On a 3.x ZooKeeper cluster, a topic could stay marked for deletion until every replica finished removing its log. An offline broker stretched that wait. Kafka 4 does not have that znode. Brokers drop local logs once metadata says the topic is gone. The remaining delay is the few seconds before every broker has seen that update.

What happens to data and consumer offsets when a topic is deleted?​

Deleting the topic deletes the log on every replica. An in-cluster replica is not a spare copy. Followers hold the same log, and the delete removes that log too. A MirrorMaker mirror is a second live log on another cluster. It is not a snapshot of the topic from before the delete.

Segment files do not always unlink in the same instant the name disappears. Topic config file.delete.delay.ms is "The time to wait before deleting a file from the filesystem." The topic configs default it to 60000 (1 minute). The topic is already gone from metadata. The files follow on that delay. You cannot read them back through Kafka while they wait.

Committed offsets do not survive either. offsets.retention.minutes defaults to 10080, which is 7 days. That timer is not what runs on a topic delete. The same page covers the delete-topic request in a separate clause. Once the metadata update propagates, any group's committed offsets for that topic are deleted. The docs call that out as happening without extra retention period.

A consumer that still uses the group will not resume at the old offset. The offset is gone with the topic. If a client recreates the name, the group falls through to auto.offset.reset.

Can you recover a deleted Kafka topic?​

Kafka cannot undo the delete. The way back is a backup taken earlier and stored outside the cluster. Long topic retention keeps the live log available for replay. It does not survive this delete. Neither does a replica.

OSO Kafka Backup restores a backup onto a cluster. The restore runbook recreates a deleted topic when create_topics is true. The configuration reference defaults create_topics to false, so the restore will not create a missing topic unless you set it. default_replication_factor defaults to 1 in that same reference. Set the factor you want on the new topic.

restore.yaml
mode: restore
backup_id: "prod-backup-20260810"

target:
bootstrap_servers:
- broker-1.kafka.svc:9092

storage:
backend: s3
bucket: my-kafka-backups
region: us-west-2
prefix: backups/production

topics:
include:
- orders

restore:
create_topics: true
default_replication_factor: 3
consumer_group_strategy: header-based

Find the backup, rehearse, then restore. These are the commands from that runbook.

kafka-backup list --path s3://my-kafka-backups/backups/production

kafka-backup validate-restore --config restore.yaml

kafka-backup restore --config restore.yaml

validate-restore checks the backup, the target, and the config without producing records. The runbook also keeps dry_run: true in the config until you are ready to write.

To stop the restore before a known bad write, set time_window_end to Unix milliseconds. The point-in-time recovery guide shows how that window is applied. The live consumer offsets were deleted with the topic. consumer_group_strategy: header-based places groups from offsets the backup stored in record headers.

The backup and restore guide compares the CLI with a Connect S3 sink and a consumer script. Take the backup before the incident. The quickstart is the short path to a first one.

How do I delete messages without deleting the topic?​

You have two supported ways to drop records and keep the topic name.

retention.ms limits how long the log is kept under the delete cleanup policy. The topic configs default it to 604800000 (7 days). Set it to -1 and there is no time limit. The same page says retention.ms can roll a new segment when its condition is met. cleanup.policy defaults to delete, which discards old segments once retention time or size is reached. Cleaning still happens a file at a time.

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

That example keeps about one hour, then lets retention discard older segments. Put the previous value back when you are done. This does not delete the topic.

kafka-delete-records.sh deletes records before an offset you choose. The tool in Kafka 4.1 reads a JSON file:

{"partitions":[{"topic":"foo","partition":1,"offset":1}],"version":1}
bin/kafka-delete-records.sh --bootstrap-server localhost:9092 \
--offset-json-file offsets.json

RecordsToDelete.beforeOffset deletes every record before that offset. The topic stays. The log start offset moves forward. DELETE_RECORDS (API key 21) uses the same Delete operation on the Topic resource as DELETE_TOPICS. An ACL that blocks topic delete also blocks this tool.

GoalWhat you runTopic remainsData left on the cluster
Remove the topickafka-topics.sh --deleteNoNo. Replicas go with the topic
Drop records before an offsetkafka-delete-records.shYesRecords before that offset are gone
Expire old segmentsretention.ms on a delete topicYesSegments past retention are gone
Get the data back after a deleteRestore a backup taken earlierRecreated on restoreOnly what the backup holds

How do I protect topics from accidental deletion?​

Start with the authorizer, not with a hope that nobody knows the command. The ACL guide says that if no pattern matches a resource, nobody but super users can access it. Do not grant Delete on topics that operators should not remove. Watch super.users and allow.everyone.if.no.acl.found. Either one can make a careful topic ACL irrelevant.

Use an explicit deny when a broader allow already covers the principal. The CLI supports --deny-principal for that case:

bin/kafka-acls.sh --bootstrap-server localhost:9092 \
--add --deny-principal User:Alice \
--operation Delete --topic orders

That deny covers both topic delete and delete-records, because both APIs use the Delete operation. It does not stop a principal who is allowed Delete on the Cluster resource. It does not stop a super user.

delete.topic.enable=false rejects topic deletes for the whole broker. That is the blunt switch. It is read-only, so turning it off or on takes a restart. It does not block kafka-delete-records.sh. It also blocks the deletes you meant to run, until you turn it back on and restart again.

None of those controls bring data back. A delete that gets through is final on the cluster. A backup taken beforehand is the copy that still exists. Restore it with create_topics: true, as the runbook above shows.

FAQ​

Frequently asked questions

Does deleting a Kafka topic delete the data immediately?

Yes. A successful delete removes the topic and every replica of its log. Segment files then wait for file.delete.delay.ms, which defaults to 60000 (1 minute), before they are removed from disk. List and describe can still show the name for several seconds.

What does delete.topic.enable do in Kafka 4?

It defaults to true. When it is false, the broker rejects the delete instead of removing the topic. Kafka 4.1 and later document that rejection. The setting is read-only, so changing it needs a broker restart. The Kafka 4.0 config page still says a disabled delete has no effect, but the 4.0 controller throws instead.

Can I undo a Kafka topic deletion?

No. Kafka has no undo for a completed topic delete. Recovery means replaying a backup taken before the delete, with create_topics set so the topic is created again. In-cluster replicas are deleted with the topic, so they are not a copy you can restore.

How do I delete all messages in a Kafka topic?

Lower retention.ms so old segments expire, or run kafka-delete-records.sh to delete records before a chosen offset. Neither command removes the topic. retention.ms defaults to 604800000 (7 days) and applies when cleanup.policy is delete.

Do consumer group offsets survive topic deletion?

No. When the topic-deletion metadata update propagates, Kafka deletes every group's committed offsets for that topic. It does not wait for offsets.retention.minutes, whose default is 10080 (7 days). That timer covers empty groups and unused subscriptions, not a topic delete.

Why did my deleted Kafka topic come back?

A client recreated it. auto.create.topics.enable defaults to true, so a produce or metadata request for a missing name creates the topic again. The new topic is empty and uses broker defaults. Stop the client, or turn auto-create off, and then delete.

Conclusion​

Deleting a Kafka topic is one command, and the cluster treats it as final. kafka-topics.sh --delete removes the topic, its replicas, and the committed offsets for it. delete.topic.enable is the broker switch that rejects that request, and it defaults to true. If the name comes back, a client with auto-create on probably made a new empty topic. If the data has to come back, restore a backup that was taken before the delete.

Need the topic back after a delete?

A backup outside the cluster is the copy Kafka itself does not keep. Restore a deleted topic from that backup, or take a first backup before the next accident.