Message Queues Compared: SQS, Kafka, RabbitMQ, Redis Streams
Message queues decouple the production of work from its consumption: a producer writes a message to the queue, and a consumer reads and processes it independently. The choice between SQS, Kafka, RabbitMQ, and Redis Streams determines the delivery semantics, retention behavior, throughput ceiling, and operational complexity of your async processing infrastructure. Each tool has a different performance profile and a different use case where it is the natural fit.
Written by Yashveer Singh, founder of Yashveer Labs.
What you need to know
- SQS is the default choice for AWS architectures that need managed, reliable message delivery without operational overhead. Start here unless you have specific requirements it cannot meet.
- Kafka is the right choice for high-throughput event streaming where multiple consumers need to process the same events independently, and where message retention and replay are requirements.
- RabbitMQ provides more sophisticated routing than SQS and is appropriate for complex message routing scenarios that SQS cannot express cleanly.
- Redis Streams is the pragmatic choice for teams already operating Redis who want a lightweight job queue without adding a new infrastructure component.
- The complexity order from lowest to highest operational overhead is: SQS, Redis Streams, RabbitMQ, Kafka (self-hosted).
The core argument
The message queue selection decision is primarily about operational complexity versus capability. SQS is a managed service: AWS runs it, scales it, and handles the operational complexity. The tradeoff is that SQS has limited routing capabilities, does not retain messages after consumption, and has a message size limit that matters for some use cases. For the majority of SaaS background job requirements, these limitations do not apply, and SQS is the pragmatic choice.
Kafka enters the picture when the use case requires capabilities SQS cannot provide: high-throughput event streaming, message retention beyond delivery, or multiple independent consumers processing the same stream. These requirements are real for data-intensive products, analytics pipelines, and event sourcing architectures. They are not real for most SaaS background job use cases. The mistake is choosing Kafka because it sounds like the right enterprise architecture when the actual use case is sending email confirmations and processing webhooks. In my experience, the teams that adopt Kafka at seed stage spend six months fighting the operational complexity before they have the scale to justify it.
Redis Streams occupies a useful middle ground. A product that already uses Redis for caching can add a message queue with no new infrastructure by using Redis Streams with consumer groups. The producer appends to a stream with XADD, the consumer reads with XREADGROUP, and the consumer acknowledges processed messages with XACK. The failure handling pattern is similar to SQS: unacknowledged messages can be reclaimed by other consumers after a timeout. The ceiling is lower than Kafka and the routing is simpler than RabbitMQ, but for moderate-volume queues within a service boundary, the zero-new-infrastructure advantage is significant.
Common mistakes
- Choosing Kafka for background job processing. Kafka is not a job queue. It is an event streaming platform. Using it for standard background job processing introduces Kafka's operational complexity without leveraging its streaming and retention capabilities. Use SQS, RabbitMQ, or a managed platform for job queues.
- Not configuring dead letter queues on SQS. An SQS queue without a DLQ sends messages that exceed the maximum receive count to the void. Configure a dead letter queue before processing any production messages.
- Not planning for SQS visibility timeout. The SQS visibility timeout determines how long a message is hidden from other consumers while being processed. If a job takes longer than the timeout, the message becomes visible again and is processed twice. Set the visibility timeout to be longer than the maximum expected job execution time.
- Running a single Kafka broker in production. A single Kafka broker has no fault tolerance. Kafka requires at least three brokers for production reliability. This minimum three-broker requirement is part of the operational cost that makes Kafka inappropriate for small-scale use cases.
- Not designing for idempotency across all queue types. All four queue systems provide at-least-once delivery under certain conditions. A consumer that receives the same message twice must handle it without corrupting data. Idempotent message handlers are a requirement regardless of which queue you use.
Where to start
- Start with SQS if you are on AWS and do not have specific requirements that exclude it. Create a queue with a matching DLQ, configure a visibility timeout appropriate for your job duration, and deploy a simple consumer. This takes two hours and works for most startup background job needs.
- Evaluate Redis Streams if you already operate Redis. If SQS is not available (non-AWS infrastructure) or if you want to minimize the number of managed services, Redis Streams covers standard job queue patterns with the Redis installation you already have.
- Only consider Kafka when you need event streaming semantics. If you need multiple consumer groups reading from the same stream, message replay, or high-throughput event ingestion, evaluate Confluent Cloud or MSK (Managed Streaming for Apache Kafka) before running Kafka yourself.
Related reading
Frequently asked
Why I am built for this project type
I have worked on five production systems before turning eighteen. That is not a flex. That is a statement of capability. Yashveer Singh, founder of Yashveer Labs. The work in this article is the work I do on a weekly basis. If you are facing the problem I just described, I do not need to be sold on solving it. I need to be told the constraints.
Posts that line up with this one.
- Backend, APIs, and System Design
Idempotency Keys: A Pattern Every Senior Engineer Should Master
Idempotency keys are a small implementation with an outsized impact on system reliability. Here is the pattern, the edge cases, and the production pitfalls that most introductions skip.
- Backend, APIs, and System Design
JSON Columns in Postgres: When They Make Sense
JSON columns in Postgres are genuinely useful for flexible, semi-structured data. They are also frequently misused as a shortcut to avoid schema design. Here is when to use them and when to use normalized tables instead.
- Backend, APIs, and System Design
Kafka in 2026: When You Need It and When You Do Not
Kafka is powerful, but most startups reach for it before they need it. Here is how to decide.
- Backend, APIs, and System Design
Lambda Cold Starts: Why They Still Matter in 2026
Cold starts have improved significantly but have not been eliminated. Here is the current state of cold start latency, which use cases still require mitigation, and the practical patterns that keep them from affecting users.