IBM MQ vs Kafka: When Kafka Replaces a Queue, and When It Cannot
Most people who search for this comparison did not arrive at it on their own. They were told to get off IBM MQ — by a cloud migration programme, a licence renewal, or an architect who wants one messaging technology instead of three — and Kafka was named as the destination before anyone checked whether it was the right one.
That framing is the problem. IBM MQ vs Kafka is not a comparison of two implementations of the same idea. One is a message broker built around queues, where each message is handed to exactly one consumer and then forgotten. It does publish/subscribe too, but subscriptions are registered before a message arrives, not after. The other is a partitioned, append-only log that keeps every message for a retention period and lets any number of consumers read it independently. They overlap enough to look substitutable and differ enough that swapping one for the other quietly removes capabilities your applications already depend on.
This article is about which capabilities those are. It covers what each system actually gives you, the six things that break when you replace a queue with a log, how the providers compare on Jakarta Messaging compliance, where the realistic IBM MQ alternatives sit, what the cost comparison actually consists of, and how to choose between them.
MQ vs Kafka: Which Problem Are You Actually Solving?
Before comparing features, work out which of these three statements describes your situation. In an IBM MQ vs Kafka evaluation the answer decides everything that follows.
| If your actual problem is… | Then the honest answer is… |
|---|---|
| The IBM MQ licence renewal, and the workload is ordinary point-to-point queueing | Almost any JMS provider will do — but check its compliance level first, in the table below. Kafka is one of the more expensive ways to solve this, in operational terms |
| You need a durable, replayable event log — event sourcing, change data capture, stream processing | Kafka, and IBM MQ was never going to give you this |
| You have a transactional workload that coordinates a database and the broker in one unit of work | Kafka cannot enlist your database. The move means the outbox pattern and at-least-once semantics everywhere — cost that before you commit |
The first row is where most of these projects actually sit, and it is the row where Kafka is most often the wrong choice. A message queue and a log are different data structures before they are different products. If you want the longer version of that argument on its own, we have written it up separately in Is Kafka a Message Queue?
What IBM MQ Actually Gives You
IBM MQ — renamed from WebSphere MQ in 2014, and MQSeries before that, which is why the same product appears under three names in older documentation — is message-oriented middleware whose design centre is assured delivery: a persistent message is not lost and is not silently duplicated by the transport, even when a machine fails mid-transaction. A message consumed in a unit of work that then rolls back is redelivered, which is what backout counts are for.
The capabilities that follow from that design centre are worth naming precisely, because they are the ones at risk in a migration:
- Transactional messaging with external resources. A queue manager can participate in an XA two-phase commit alongside a relational database, and can act as the transaction coordinator itself. Put a message on a queue and update a table in one atomic unit; both commit or neither does.
- Per-message acknowledgement and redelivery. Messages are consumed inside a JMS session, and the session decides when the acknowledgement happens. A rollback returns the message to the queue with an incremented backout count. The threshold and destination live on the queue as the
BOTHRESHandBOQNAMEattributes, though the requeue itself is performed by the MQ JMS client or resource adapter rather than the queue manager. - Reach beyond the JVM. Native clients for C, COBOL, .NET and PL/I, and first-class z/OS support. For a mainframe estate this is frequently the actual reason MQ is still there.
- Queue depth as an operational primitive. The number of messages waiting on a queue is a directly observable, alertable number, and operations teams build years of runbooks around it. Kafka’s nearest equivalent is consumer lag, which measures a group’s position rather than a queue’s contents.
- Jakarta Messaging compliance. IBM MQ classes for Jakarta Messaging implement the specification, which means selectors, durable subscribers, priorities and the rest of the standard API surface are available rather than approximated.
What IBM MQ does not give you is retention and replay. Once a message is consumed and acknowledged, it is gone. There is no offset to rewind and no way to reprocess last Tuesday's traffic through new code. Durable subscriptions and streaming queues will fan a message out to several independent consumers, but only to consumers configured before it arrived (retained publications are the one exception, and they keep only the latest message per topic). Throughput is also in a different class from Kafka's — MQ is built for reliability per message, not for volume.
What Kafka Actually Gives You
Kafka is a distributed, partitioned, append-only log. Producers append records to a topic partition; consumers track their own position in that partition by offset. Nothing is removed when it is read. Records are removed by a cleanup policy — aged out by time or size, or compacted so that only the latest value per key survives — and until then anyone can read them again. There is no queue and no publish/subscribe topic in the JMS sense here: a Kafka topic behaves as either, depending on how consumers are grouped — one consumer group competing for records is a queue, several groups reading independently is pub/sub.
That single design decision produces everything Kafka is good at. Replay is free, because reading does not consume. Multiple independent consumer groups can read the same topic at different speeds without interfering, which is what makes Kafka a natural backbone for event-driven architecture rather than merely for work distribution. Throughput scales by adding partitions, and because writes are sequential appends to disk, the ceiling is high. Kafka vs IBM MQ on raw throughput is not a close contest, but the size of the gap is routinely overstated. Published Kafka figures run into millions of records per second for a small cluster. IBM’s own V9.4 performance report tops out near 120,000 round trips per second for 2KB persistent messages on a single queue manager, and around 370,000 non-persistent — and a round trip is two messages. Most of the Kafka numbers come from companies selling Kafka platforms, and the two are not measured the same way, since MQ’s persistent figures are fsynced per commit. The gap is real and it widens as you add partitions and brokers, but on a per-broker basis it is a multiple, not the cliff the marketing implies.
Kafka's ordering guarantee is per partition, not per topic — a detail that matters more in migration than people expect, because MQ's ordering model is per queue. If your consumers rely on strict ordering across a whole logical stream, you are constrained to one partition; one partition means one consumer per consumer group, and the throughput advantage disappears. Share groups do not rescue this, because they give up ordering to get the extra consumers.
Kafka has also moved toward queueing. Share groups, introduced under KIP-932 and generally available in Apache Kafka 4.2, let multiple consumers cooperatively process a topic regardless of partition count, with broker-held acquisition locks (30 seconds by default) and per-record acknowledgement — accept, release, reject, or — in explicit acknowledgement mode — renew to extend the lock on a record still being processed. This is a genuine change to the comparison. Note the trade, though: share groups deliberately sacrifice ordering for elastic scaling, dead-letter support and exactly-once semantics are planned rather than shipped, and at GA only Java clients were supported — librdkafka has since added a preview C API, but production non-Java support is still pending, which matters a great deal if the estate you are migrating is the polyglot one described in the previous section.
The log shape is not unique to Kafka, incidentally — Redis Streams implements the same append-only, consumer-group model at a smaller scale. If your interest is Kafka against Redis rather than against MQ, that is a different comparison and we have it separately in Redis vs Kafka: When to Use Each. For the wider field, see Apache Kafka Alternatives.
Six Things That Break When You Swap a Queue for a Log
This is the section the migration plan usually skips. Each of these is a capability that exists in IBM MQ and is used by real applications. Five of the six still have no Kafka equivalent; the sixth, per-message acknowledgement, only became viable in Kafka 4.2, and with conditions attached.
| Capability | IBM MQ | Kafka | Cost of the workaround |
|---|---|---|---|
| Message selectors | SQL92-style filter on headers and properties, evaluated broker-side | No equivalent | Consume everything and filter client-side, or split into many topics |
| Per-message ack and redelivery | Session-based ack; backout count and backout queue | Share groups (4.2+) give accept/release/reject/renew under a 30s lock | Newly viable, but no dead-letter support yet, and ordering is given up |
| XA with a database | Full two-phase commit across broker and JDBC resource | Transactions are internal to Kafka only | Outbox pattern plus a relay — real work, real code |
| Message priority | Priority 0–9, honoured at delivery | No equivalent | Separate topics per priority and a consumer that drains them in order |
| Delayed delivery | JMS 2.0 delivery delay (JMS clients only, MQ 8.0+) | No equivalent | An external scheduler, or a delay topic per interval |
| Queue browsing | Inspect individual messages without consuming | No equivalent | Read the partition from an offset and discard — not the same operation |
Three of these deserve more than a table row.
Selectors are the one people forget. A JMS consumer can say "give me only the messages where region = 'EMEA' and JMSPriority > 5" and the broker evaluates that expression before it sends anything. Kafka has no server-side filtering at all. The replacement is either to consume the entire topic and discard most of it, which wastes bandwidth in proportion to how selective the filter was, or to fan out into one topic per predicate, which turns a runtime filter into a deployment-time topology decision. Neither is free, and neither is a small change to the consuming application.
XA is where migrations get abandoned. Kafka's transaction support is real, but it covers reads and writes within Kafka — the read-process-write cycle — and cannot enlist a JDBC connection in the same unit of work. If your application currently writes a database row and sends a message atomically, moving to Kafka means adopting the outbox pattern: write the message into a table in the same local transaction, then have a separate relay publish it. That works, and it is well understood, but it is an application rewrite plus a new moving part, and it changes your delivery semantics to at-least-once, which means every consumer now needs to be idempotent. Budget for it explicitly — and note that this is the price of moving to Kafka, not the price of leaving MQ. A provider that implements XAResource keeps the two-phase commit and skips the outbox entirely. Our background on the underlying mechanics is in XA transactions and distributed transactions.
Redelivery and dead-lettering are not at parity. Row two is the one that changed, and it changed only halfway. IBM MQ has had backout queues for decades, and the dead letter queue is a standard part of any JMS design. Kafka's classic consumer groups have never had a native DLQ — Connect's errors.deadletterqueue.topic.name is a Connect feature, not a broker one, and frameworks such as Spring Kafka provide theirs at the client layer — and share groups do not have one yet either. KIP-1191 went to a vote at the end of January 2026 and has since been accepted, gated behind the share.version=2 feature version and targeting Kafka 4.4; until it lands, a rejected record transitions straight to the archived state and is simply discarded. The real contrast is that MQ’s poison-message policy is declared on the queue itself, so every consumer of that queue inherits the same threshold and destination — even though the requeue is executed client-side. The Kafka equivalents are per-application. If your design leans on that, check what you are actually getting before you commit.
JMS Compliance, Provider by Provider
Here is the fact that reframes this entire comparison: Kafka is not a Jakarta Messaging provider at all. Not a partial one, not an old one — it does not implement the specification. JMS-to-Kafka bridges exist, and they are useful, but they emulate the API over a fundamentally different system rather than implementing it. A bridge can only evaluate a selector after the record has already crossed the network — which is the cost the selector existed to avoid.
If your applications are written against jakarta.jms or javax.jms, that single fact is worth more than any throughput chart. Moving from IBM MQ to another fully compliant JMS provider is close to a configuration change — with the caveat in the next paragraph. Moving from IBM MQ to Kafka is a rewrite of every producer and consumer.
| Provider | Highest spec level | Namespace | Deployed as | High availability |
|---|---|---|---|---|
| IBM MQ | Jakarta Messaging 3.0 / JMS 2.0 — full | jakarta.jms or javax.jms, per client library | Native queue manager | Multi-instance queue managers, Native HA, RDQM |
| RabbitMQ | Jakarta Messaging 3.0 / JMS 2.0 via a separate JMS client over AMQP — partial (no XA; queue selectors unimplemented) | Depends on client | Erlang cluster | Quorum queues and streams (classic mirroring removed in 4.0) |
| Apache Kafka | Not a JMS provider | — | Broker cluster (KRaft) | Partition replication |
| Redisson PRO | Jakarta Messaging 3.1, 3.0, JMS 2.0 — full, all TCK tests pass | Both | Client library on existing Redis or Valkey | Inherited from Redis/Valkey — Sentinel, Cluster, replication |
Cost and Operations: What You Are Really Comparing
The comparison people expect here is a licence fee against zero. That is not the comparison, and it is why most shortlists of IBM MQ alternatives are built on the wrong number.
IBM MQ is licensed commercially, historically by processor value unit and more recently through virtual processor core and container-aware metrics. The number is real, it is usually large, and it is the reason most of these evaluations start. What it buys is a supported product with a vendor on the other end of a severity-one call.
Kafka's licence cost is zero and its operational cost is not. Running Kafka yourself means a broker cluster, storage sized for your retention policy, and people who understand partition rebalancing, consumer lag, and what to do when a broker's disk fills at 3am. KRaft has removed the ZooKeeper dependency, which is a genuine simplification, but it removed a component rather than the discipline. The realistic comparison is the MQ licence against a managed Kafka bill plus engineering time, or against the fully loaded cost of the team that runs your own cluster. That is the number to put beside any of the IBM MQ competitors on your shortlist.
We are deliberately not publishing dollar figures. Both IBM's metrics and every managed Kafka price list change often enough that any table here would be wrong within a quarter. Two relationships are more durable than any number:
- Retention drives Kafka's storage cost almost linearly. A seven-day retention policy on a high-volume topic is a different product, financially, from a one-day policy. This is a knob MQ does not have, because MQ does not retain.
- Migration cost scales with how many of the six capabilities above you actually use. A shop using plain point-to-point queues with no selectors, no XA and no priority can move for the price of the client rewrite. A shop using all three is looking at an application redesign, and that cost can easily exceed several years of licence fees.
That is the uncomfortable conclusion: the teams for whom the licence saving is largest are frequently the teams whose migration is most expensive, because heavy MQ licensing tends to correlate with heavy use of the MQ features that do not port.
If Your MQ Workload Is Really Just Queues and Topics
There is a middle case worth naming, because it is common and it is badly served by the Kafka-or-nothing framing.
Plenty of IBM MQ estates are not doing anything exotic. They send messages to queues, they subscribe to topics, they have a dead-letter path, and they have no XA transaction anywhere in the codebase. For those workloads the requirement is not a streaming platform — it is a JMS provider that costs less to run. And if there is already a Redis or Valkey instance in the stack for caching or sessions, the messaging can go there without introducing another system to operate — though it does mean sizing and isolating that instance for messaging traffic, not just cache.
That is what Redisson PRO's JMS API implementation does. It implements Jakarta Messaging 3.1, Jakarta Messaging 3.0 and JMS 2.0 (JSR 343), and successfully passes all TCK tests. Point-to-point queues map to RReliableQueue and publish/subscribe topics to RReliablePubSubTopic, both backed by the Redis or Valkey deployment you already operate. Configuration is available through the native Java API or declaratively through JNDI with RedissonInitialContextFactory, so an application server deployment does not need code changes.
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.api.queue.QueueConfig;
import org.redisson.config.Config;
import org.redisson.jms.RedissonConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import java.time.Duration;
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RedissonConnectionFactory cf = new RedissonConnectionFactory(redisson);
cf.setClientId("orders-app"); // required for durable subscriptions
cf.setQueueConfig("orders", QueueConfig.defaults()
.deliveryLimit(5) // max delivery attempts before dead-lettering
.visibility(Duration.ofSeconds(60)) // how long a polled message stays invisible
.deadLetterQueueName("orders-dlq"));
JmsTemplate jms = new JmsTemplate(cf);
jms.convertAndSend("orders", "order-123");
The features that map most directly onto MQ habits are the ones worth checking against your own usage: deliveryLimit and deadLetterQueueName cover backout counts and backout queues; visibility and timeToLive cover message expiry and in-flight timeouts; durable subscriptions are created with createDurableConsumer; and producer-side setPriority and setDeliveryDelay cover two of the five capabilities in the table above that Kafka still has no answer for. There is more detail in JMS Messaging Over Valkey and Redis, Reliable Queue, Reliable PubSub and dead letter queues and topics.
To be equally clear about the boundaries. There are no wire-protocol clients, so a COBOL or .NET estate talking to MQ natively is out of scope; this is a JVM client library. Retention is memory-bound rather than disk-based, so it is not a replacement for Kafka's replayable log either. Message selectors are covered by the TCK, so they work — but the documentation does not say where they are evaluated, and a client library on Redis is not a broker; if your selectors do heavy filtering, measure it rather than assuming the server-side behaviour described earlier. And the JMS API is a Redisson PRO feature, not a Community one.
On XA, though, Redisson PRO is not in the same position as Kafka. It ships an XAResource implementation: redisson.getXAResource() enlists in a JTA global transaction, so a write to a Redis-backed map, set or bucket commits or rolls back together with your database at READ_COMMITTED isolation. That is precisely the capability the outbox pattern exists to work around, and it is genuinely there. What the documentation does not yet state is whether a JMS send to a queue or topic joins that same global transaction — the objects it lists are RMap, RMapCache, RLocalCachedMap, RSet, RSetCache and RBucket. If your requirement is specifically an atomic database-write-plus-message-send, confirm that before designing around it.
How to Choose Between IBM MQ, Kafka and the Alternatives
| Your binding constraint | Choose |
|---|---|
Existing applications written against jakarta.jms or javax.jms | Not Kafka. It does not implement the specification, so every producer and consumer is a rewrite rather than a reconfiguration |
| Mainframe, COBOL, or a .NET estate written against MQ’s native or XMS API | Stay on IBM MQ. Nothing else here reaches z/OS or COBOL, and a native-MQ .NET client is a rewrite whichever target you pick |
| Durable replayable log, event sourcing, stream processing | Kafka. This is the job it was built for, and MQ never did it |
| Very high sustained throughput across many consumers | Kafka |
| JMS queues and topics, and the licence is the problem | JMS provider — Redisson PRO if Redis or Valkey is already deployed |
| Genuinely both a log and a queue | Both. That is a legitimate architecture, and it is more common than replacing one with the other |
The expensive mistake is rarely the initial pick. It is adopting a streaming platform to solve a queueing problem and then carrying its operational cost for years — the mirror image of keeping an expensive broker for a workload any provider could handle.
Frequently Asked Questions
Is Kafka a replacement for MQ?
Sometimes, but not by default. Kafka replaces a message queue well when the workload is high-volume, when consumers benefit from replay, and when strict ordering across a whole stream is not required. It replaces one badly when the application depends on broker-side message selectors, message priority, delayed delivery, or XA transactions spanning the broker and a database, because Kafka has no equivalent for any of those. Kafka 4.2's share groups closed part of the gap by adding per-message acknowledgement (accept, release, reject or renew) and competing consumers, but dead-letter support and exactly-once semantics for share groups are not shipped yet.
What is the main difference between Kafka and a message queue?
A message queue delivers each message to one consumer and deletes it once acknowledged. Kafka is an append-only log: reading does not consume, records stay until a retention policy removes them, and every consumer group tracks its own position by offset. That single difference produces everything else — Kafka can replay history and a queue cannot, while a queue can browse individual messages and filter them server-side, which Kafka cannot do. Queue depth has a rough Kafka analogue in consumer lag, though that measures a group’s position rather than a queue’s contents.
What is IBM MQ called now?
It is called IBM MQ. The product was named MQSeries at launch, became WebSphere MQ in 2002, and was renamed IBM MQ in 2014 with version 8. All three names refer to the same product line, which is why older documentation and job adverts use them interchangeably. The Java API is delivered as IBM MQ classes for JMS and IBM MQ classes for Jakarta Messaging.
Can Kafka replace IBM MQ?
It depends almost entirely on which IBM MQ features you use. A workload of plain point-to-point queues with no selectors, no priorities and no XA can move to Kafka for the cost of rewriting producers and consumers. A workload that coordinates the broker and a database in one transaction, or filters messages broker-side, or relies on z/OS and native COBOL clients, cannot move without an application redesign. Note also that Kafka is not a Jakarta Messaging provider, so applications written against the JMS API are rewritten rather than reconfigured.
Which one is better, Kafka or RabbitMQ?
They are built for different jobs. Kafka is better for durable replayable event streaming and very high throughput. RabbitMQ is better for complex routing topologies, per-message acknowledgement and traditional queueing, and it is considerably lighter to operate. If you are choosing between them for a queueing workload, RabbitMQ is usually the better fit; if you need to replay history or run stream processing, Kafka is.
Is RabbitMQ the same as IBM MQ?
No. IBM MQ vs RabbitMQ is a closer contest than either against Kafka: both are message brokers with queues and publish/subscribe. But RabbitMQ is open-source, written in Erlang, and speaks AMQP natively with JMS available through a separate client library. IBM MQ is commercial, has native clients for C, COBOL, .NET and z/OS, implements Jakarta Messaging directly, and can act as an XA transaction coordinator. RabbitMQ is the closer functional match to IBM MQ than Kafka is, but it is not a drop-in replacement.
Is MQTT the same as IBM MQ?
No, though the names invite the confusion. MQTT is a lightweight publish/subscribe wire protocol designed for constrained devices and unreliable networks, typically used for IoT telemetry. IBM MQ is a full messaging product. They are related historically — MQTT was co-invented by an IBM engineer and a partner at Arcom Control Systems — and IBM MQ can act as an MQTT broker through its telemetry service, but MQTT is a protocol and IBM MQ is a product that speaks several protocols.