Choosing between Kafka, SQS, and RabbitMQ comes down to throughput needs, ordering guarantees, and how much operational overhead your team can absorb. Each solves the same broad problem—decoupling producers from consumers—but they diverge sharply in delivery semantics, scaling model, and failure behavior.
This article compares the three from a message queue architecture perspective: how messages are stored, routed, retried, and consumed at scale. You'll get concrete numbers (limits, ports, retention defaults), architectural diagrams described in text, and guidance on when each tool is the wrong choice, not just the right one.
By the end, you'll be able to answer the common Kafka vs SQS debate with actual trade-offs instead of vibes, and understand where RabbitMQ still fits in a landscape dominated by managed cloud services.
What problem does a message queue actually solve?
A message queue decouples the component that produces work (a producer) from the component that processes it (a consumer), so each can scale, fail, and deploy independently. Without this decoupling, a slow downstream service (say, a payment processor having a bad day) directly stalls the upstream API that called it synchronously.
In a typical message queue architecture, producers publish messages to a broker, the broker persists them (in memory, on disk, or in a distributed log), and consumers pull or receive them asynchronously. The broker also handles retry logic, dead-letter routing, and often ordering guarantees. What differs between Kafka, SQS, and RabbitMQ is how they implement persistence and delivery — and that difference cascades into everything from cost to failure modes.
Amazon SQS: the fully managed queue
Amazon SQS is a managed, pull-based queue service with no brokers to patch, no cluster to size, and a simple at-least-once delivery model — ideal when you want decoupling without operational overhead.
SQS comes in two flavors: Standard queues (near-unlimited throughput, best-effort ordering, possible duplicate delivery) and FIFO queues (strict ordering per message group, exactly-once processing within a 5-minute deduplication window, capped at 3,000 messages/sec per API action with batching, or 300/sec without).
Key mechanics:
- Messages are retained up to 14 days (configurable, default 4 days).
- Visibility timeout (default 30 seconds, max 12 hours) hides a message from other consumers while it's being processed; if the consumer doesn't delete it in time, it reappears — a common source of duplicate processing bugs.
- Dead-letter queues (DLQs) capture messages that fail processing after a configurable maxReceiveCount.
- Long polling (ReceiveMessageWaitTimeSeconds up to 20s) reduces empty-response API calls and cost versus short polling.
- No native pub/sub fan-out — for that you pair SQS with SNS (SNS topic fans out to multiple SQS queues) or use EventBridge for more complex routing.
SQS shines for task queues: background jobs, order processing pipelines, decoupling Lambda functions, or buffering writes ahead of a database. It's a poor fit when you need multiple independent consumer groups to replay the same stream of events, or when message ordering across the entire topic (not just a group) matters.
Apache Kafka: the distributed commit log
Apache Kafka is a distributed, partitioned, replicated commit log designed for high-throughput event streaming where multiple consumers need independent, replayable access to the same data.
Unlike SQS or RabbitMQ, Kafka doesn't "delete" messages once consumed. Messages live in topics split into partitions, each an append-only log stored on disk (and now, with KRaft mode replacing ZooKeeper as of Kafka 3.x/4.x, metadata is managed by the brokers themselves). Consumers track their own read position (the offset) per partition, so ten different consumer groups can independently replay the same topic from different points without affecting each other.
Key mechanics:
- Producers publish to a partition key; all messages with the same key land in the same partition, preserving order within that partition (not across the whole topic).
- Replication factor (commonly 3) copies each partition across brokers; acks=all plus min.insync.replicas=2 gives durability guarantees against broker loss.
- Default retention is time-based (commonly 7 days) or size-based, independent of whether messages have been consumed — this is what enables replay for debugging, reprocessing, or onboarding new consumers.
- Kafka runs on port 9092 (plaintext) or 9093 (TLS) by default; Kafka Connect and Kafka Streams extend it into an ETL/stream-processing platform.
- Amazon MSK (Managed Streaming for Apache Kafka) and MSK Serverless offload cluster operations, though you still manage topic/partition design, IAM policies, and client tuning.
Kafka's throughput can reach millions of messages per second across a well-partitioned cluster (LinkedIn, Kafka's birthplace, has published cluster figures in the trillions of messages/day historically). But that power comes with real complexity: partition count decisions are hard to change later, consumer group rebalancing can cause processing pauses, and running your own cluster means managing broker disk I/O, page cache tuning, and ISR (in-sync replica) health.
RabbitMQ: the flexible message broker
RabbitMQ is a general-purpose message broker built around the AMQP 0-9-1 protocol, offering flexible routing (direct, topic, fanout, headers exchanges) that neither SQS nor Kafka natively provides.
RabbitMQ's core abstraction is the exchange, which routes messages to one or more queues based on routing keys and bindings. This gives you patterns like:
- Fanout exchange: broadcast to all bound queues (like SNS, but self-hosted).
- Topic exchange: route by wildcard pattern (e.g., orders.us.*).
- Direct exchange: exact routing-key match, useful for simple point-to-point work queues.
Key mechanics: - Runs on port 5672 (AMQP), 5671 (AMQPS/TLS), with a management UI/API on 15672. - Supports publisher confirms and consumer acknowledgments for reliable delivery, plus quorum queues (Raft-based, replacing the older, less reliable mirrored queues) for durability across a cluster. - Native priority queues, message TTL, delayed message plugin, and per-queue dead-lettering — richer routing semantics than SQS out of the box. - Deleted from the queue once acknowledged — no built-in replay like Kafka. If you need audit/replay, you must add that yourself (e.g., logging to S3 or a separate store). - Throughput is solid for a broker (tens of thousands of messages/sec per node is realistic) but generally lower than a well-tuned Kafka cluster at extreme scale, because it's optimized for flexible routing and low-latency delivery rather than sequential log throughput.
RabbitMQ is the classic choice for task distribution with complex routing logic — microservices that need topic-based fanout, priority processing, or per-message TTL without standing up a full streaming platform. It's often self-managed (Docker, Kubernetes, EC2) though Amazon MQ offers a managed RabbitMQ (and ActiveMQ) option that removes patching overhead while keeping AMQP/STOMP/MQTT compatibility.
Kafka vs SQS: the debate that actually matters
The Kafka vs SQS decision usually isn't about raw performance — both handle enormous throughput at production scale. It's about replay, ordering scope, and operational ownership.
SQS is a queue: once a consumer deletes a message, it's gone. Kafka is a log: messages persist for a configured retention window regardless of consumption, so new consumers can start from the beginning, and existing consumers can rewind after a bug fix. If your architecture needs event sourcing, CQRS read model rebuilding, or multiple independent teams consuming the same event stream (e.g., fraud detection and analytics both reading the same "order placed" events), Kafka's log model fits naturally. If you need a simple, fully managed buffer between two services with minimal setup, SQS wins on cost and simplicity.
Ordering also differs: SQS FIFO gives ordering per message group ID; Kafka gives ordering per partition. Both are essentially "ordering within a shard," but Kafka's partition count is a capacity-planning decision made upfront (repartitioning later is painful), while SQS FIFO group IDs can be assigned dynamically per request.
Comparison table
| Aspect | Amazon SQS | Apache Kafka | RabbitMQ |
|---|---|---|---|
| Model | Managed queue | Distributed log | Message broker (AMQP) |
| Ordering | FIFO: per group ID | Per partition | Per queue (with care) |
| Replay | No (message deleted on ack) | Yes (offset-based, within retention) | No (needs custom setup) |
| Max throughput | High (near-unlimited Standard) | Very high (millions/sec, cluster-dependent) | Moderate-high (tens of thousands/sec/node) |
| Ops overhead | None (fully managed) | High (self-hosted) / Medium (MSK) | Medium (self-hosted) / Low (Amazon MQ) |
| Best fit | Simple task queues, Lambda triggers | Event streaming, replay, multi-consumer fan-out | Complex routing, priority/TTL semantics |
Failure modes and delivery guarantees
Every message queue architecture must answer: what happens when a consumer crashes mid-processing? The three systems handle this differently, and understanding the mechanism (not just the marketing term "at-least-once") matters for building idempotent consumers.
In SQS, an unacknowledged message becomes visible again after the visibility timeout expires — meaning a slow consumer that takes longer than the timeout will see its own in-flight message redelivered to another worker, causing duplicate processing unless you extend the timeout via ChangeMessageVisibility or design for idempotency. In Kafka, a consumer that crashes before committing its offset will have that offset reassigned to another consumer in the group during rebalance, and messages between the last commit and the crash point get reprocessed — again, at-least-once by default unless you implement transactional/exactly-once semantics (enable.idempotence=true, transactional producers, read_committed isolation level). In RabbitMQ, an unacknowledged message (no basic.ack) gets requeued when the consumer's channel closes or a basic.nack/basic.reject is sent, and can be routed to a dead-letter exchange after exceeding a retry count or TTL.
None of the three give you true exactly-once delivery across arbitrary systems for free — "exactly-once" claims almost always mean exactly-once within the broker's own transactional boundary (e.g., Kafka's transactional API across topics it manages), not end-to-end across your database and downstream calls. Idempotent consumer design (dedup keys, upserts instead of inserts) is still necessary in all three architectures.
Cost and operational trade-offs
Cost isn't just the sticker price — it's the sum of API/data charges plus the engineering time spent operating the system.
SQS pricing is pay-per-request (per million requests, tiered by request type — Standard is currently priced lower than FIFO per the AWS pricing pages), with no idle cost and no capacity planning. This makes it attractive for spiky or unpredictable workloads. Kafka, whether self-hosted on EC2 or via Amazon MSK, incurs continuous costs: broker instance hours, EBS storage for retained data, cross-AZ replication data transfer, and (for self-hosted) the engineering time for partition rebalancing, JVM tuning, and monitoring ISR shrinkage. MSK Serverless reduces some of this by auto-scaling storage/throughput, but you still design topics and manage IAM/networking. RabbitMQ costs scale with node count and message volume; Amazon MQ removes patching burden but you still size broker instance classes and plan for active/standby failover.
A rough rule of thumb many teams use: SQS for services where message volume is unpredictable or low-to-medium and simplicity matters most; Kafka when you're already running (or planning) a data platform with multiple consumers per event and can justify a platform team; RabbitMQ when your routing logic is genuinely complex (multi-criteria fanout, priority queues) but you don't need long-term event replay.
Choosing an architecture in practice
A pragmatic message queue architecture decision tree looks like this: start by asking whether you need replay/multiple independent consumer groups over the same events. If yes, lean Kafka (or MSK). If no, and your routing is simple (one producer, one logical consumer group, maybe a DLQ), SQS is usually the lowest-friction choice, especially in an already-AWS-native stack. If you need rich routing (topic-based fanout, priority, delayed delivery) without building a streaming platform, RabbitMQ (or Amazon MQ) fits between the two.
It's also common to combine them: SNS+SQS fan-out for lightweight pub/sub inside AWS, Kafka as the backbone for cross-team event streaming and analytics pipelines, and RabbitMQ for internal microservice task queues with complex routing needs. There's no single "best" — the right call depends on team size, existing AWS footprint, and whether replay is a feature you'll actually use or a "just in case" justification for extra complexity.
Key Takeaways
- SQS is the lowest-operational-overhead option — fully managed, pay-per-request, ideal for simple task queues and Lambda-triggered pipelines, but messages are deleted once acknowledged (no replay).
- Kafka models messages as a persistent, replayable log partitioned for parallelism — best when multiple independent consumers need to read the same event stream, at the cost of real operational complexity even with MSK.
- RabbitMQ offers the richest built-in routing (fanout, topic, priority, TTL, dead-lettering) via AMQP exchanges, making it a strong fit for complex point-to-point and pub/sub microservice patterns.
- Ordering in all three is scoped, not global: SQS FIFO orders per message group, Kafka orders per partition, RabbitMQ orders per queue — none guarantee total order across the whole system without sacrificing parallelism.
- "At-least-once" is the realistic default for all three; true exactly-once requires either broker-specific transactional features or idempotent consumer logic on your side.
- The Kafka vs SQS decision hinges less on throughput and more on whether you need event replay and multi-consumer-group fan-out.
- Managed variants (MSK, Amazon MQ) reduce but don't eliminate operational responsibility — topic/partition design, IAM policies, and capacity planning are still on your team.
Frequently Asked Questions
Is Kafka faster than SQS?
Kafka can sustain higher raw throughput per cluster because it's built around sequential disk writes and partition-level parallelism, often reaching millions of messages per second at scale. SQS Standard queues also scale very high with near-unlimited throughput, so for most workloads outside extreme event-streaming use cases, both are "fast enough" — the real differentiator is replay and consumer-group semantics, not speed.
Can SQS replace Kafka?
For simple task-queue use cases — decoupling a producer from a single consumer group, buffering Lambda invocations, retrying failed jobs — SQS can replace Kafka with far less operational overhead. It cannot replace Kafka when you need multiple independent consumer groups replaying the same event history, since SQS deletes messages once acknowledged.
Does RabbitMQ support message replay like Kafka?
Not natively. RabbitMQ removes messages from a queue once acknowledged, so replay requires custom engineering, such as also writing events to S3, a database, or a separate audit log for reprocessing.
What is the difference between SQS Standard and SQS FIFO?
SQS Standard offers best-effort ordering, at-least-once delivery, and near-unlimited throughput, while SQS FIFO guarantees strict ordering per message group ID and exactly-once processing within a 5-minute deduplication window, at lower throughput (300–3,000 msg/sec depending on batching). Choose FIFO only when ordering correctness matters more than raw throughput.
Is Amazon MSK the same as running Kafka yourself?
No. Amazon MSK manages broker provisioning, patching, and (in KRaft-based clusters) metadata management, but you still design topics, partitions, replication factors, and client-side tuning yourself. MSK Serverless goes further by auto-scaling storage and throughput, reducing but not eliminating architectural decisions.
Which message queue is cheapest for a small startup?
For low-to-moderate, spiky traffic, SQS is typically cheapest because it's pay-per-request with zero idle cost and no cluster to run. Kafka and self-hosted RabbitMQ incur continuous infrastructure costs regardless of traffic volume, which usually only pays off once throughput or the need for replay/multi-consumer fan-out justifies it.
Can I use Kafka and SQS together in the same architecture?
Yes, and many production systems do — for example, using Kafka as the durable event backbone for cross-team analytics and stream processing, while using SQS (often via SNS fan-out) for simpler, service-local task queues. This hybrid approach lets teams pick the right tool per use case rather than forcing one system to do both jobs.
Draw this in seconds with draft1. Describe your architecture in plain English and draft1 generates an editable AWS/cloud diagram plus documentation — no dragging boxes around. Try it free.
Draw this in ~20 seconds
Describe your own version of this architecture and draft1 generates an editable draw.io diagram — boxes, arrows, labels, the lot.
Generate this diagram free ➔Free demo — no signup. Then 3 free diagrams with an account, no card.