Amazon SQS vs. Reliable Queue for Valkey and Redis

Last updated
August 27, 2026

Amazon SQS is a fully managed message queue and, for a great many teams, the correct answer. It has no servers to run, it scales to zero, it costs nothing when idle, and it has been in production for twenty years. If those properties are what you are buying, nothing below will change your mind, and it should not.

This comparison is for the narrower case: a JVM application that already runs Valkey or Redis, with a queueing workload that keeps colliding with an SQS limit. Redisson PRO's Reliable Queue puts broker-grade delivery semantics — acknowledgments, visibility timeouts, delivery limits, dead-letter queues, per-message priority — on the data store you already operate. The question is not which is better in the abstract. It is which limits you are hitting, and whether the trade you would be making is one you want.

Every SQS figure below is taken from current AWS documentation — the service quotas, the fair queues guide and the pricing page — and re-verified in August 2026. Several of them changed after this page was first written, and one of the changes is more expensive than it looks.

What Changed in SQS Since 2025

Three things, and the first two matter to any comparison written before them.

The maximum message payload went from 256 KiB to 1 MiB in August 2025. Any comparison table still quoting 256 KB — including the earlier version of this one — is a year out of date. This closed one of SQS's most-cited limitations outright.

It also quadrupled the ceiling on what a single message can cost. AWS bills SQS per request, and a request is not the same thing as an API call: "Each 64 KB chunk of a payload is billed as 1 request (for example, an API action with a 1 MiB payload is billed as 16 requests)." That billing rule is not new — it has applied since 2013, when the limit rose to 256 KiB and a full-size message became four billable requests. What August 2025 changed is the maximum. A full-size message is now sixteen billable requests rather than four, charged again on every API action it passes through. The larger payload is genuinely useful and it is not free, and the announcement does not mention pricing at all. Teams that raised their payload size without re-running the arithmetic will see the bill move before they see the cause.

Fair queues arrived in July 2025 for standard queues, and they are frequently mistaken for message priority. They are not. See the ordering section below — the distinction is the single most important correction on this page.

Two smaller changes round it out. Dual-stack IPv4/IPv6 endpoints landed on SQS public endpoints in April 2025. And in August 2026, AWS raised the ceiling on Lambda provisioned-mode event pollers for SQS event source mappings from 2,000 to 10,000 — a consumer-side scaling change rather than a queue feature, but the one 2026 item that affects how fast an SQS-driven pipeline can actually drain.

One caveat on the size increase, since it is easy to miss: it shipped across commercial Regions and GovCloud (US), but not the China Regions, which remain at 256 KiB. The AWS General Reference quota table also still lists 256 KB, so check the Developer Guide or the pricing page rather than that one.

Feature Comparison

Redisson PRO Reliable QueueAmazon SQS
Message size limitmaxMessageSize; unset by default, so the practical ceiling is the Valkey/Redis 512 MB value limit1 MiB (raised from 256 KiB in Aug 2025); up to 2 GB via the Extended Client Library and S3
Cost modelYour existing Valkey/Redis capacity + PRO licence. No per-message chargePer request, billed in 64 KB chunks — a 1 MiB payload is 16 requests. 1M requests/month free
Message priorityPer message, 0–9None. Fair queues give per-tenant fairness, not priority
Multi-tenant fairnessNot built in — model it with priority or separate queuesFair queues via MessageGroupId, automatic on standard queues
Strict orderingProcessingMode.SEQUENTIALFIFO queues only, and at a throughput cost
Negative acknowledgmentsYesfailed (redeliver, with delay) or rejected (straight to DLQ)None. Failure means letting the visibility timeout lapse
Acknowledgment modesMANUAL (default) or AUTOImplicit — DeleteMessage after processing
Delivery attempts limitdeliveryLimit, per queue or per message (default 10)maxReceiveCount on the redrive policy
Dead-letter queuedeadLetterQueueNameYes, via redrive policy
DeduplicationBy ID or payload hash, configurable window, on any queueFIFO queues only, by ID, fixed 5-minute window
Visibility timeoutvisibility, per queue or per poll; no documented maximum (default 30 s)Per message, max 12 hours (default 30 s)
Delayed deliveryPer message, millisecond precision, no documented maximumMax 15 minutes
Message expirationtimeToLive, per queue or per message; off by defaultRetention 4 days by default, 14 days max, 60 s min
Queue depth limitmaxSize — produces real backpressureUnlimited, so a producer never learns consumers are behind
In-flight limitGoverned by processingMode and poll count120,000 — the same on standard and FIFO queues since Nov 2024
Batch operationsAdd, poll, acknowledge, remove, move — no documented batch-size limit10 messages per request, 1 MiB total payload
Message metadataArbitrary headers, any serializable value, custom codec10 attributes per message; string and binary payloads
Long pollingBlocking poll, no documented maximum (Duration.ZERO waits indefinitely)Max 20 seconds
Standard throughputBounded by your Valkey/Redis capacityEffectively unlimited on standard queues
Ordered throughputSequential mode processes one message at a timeFIFO: 300 TPS per API action, 3,000 messages/s batched. High-throughput mode lifts this well beyond, by region and by tier
Replication controlsyncMode: AUTO, ACK, ACK_AOF; set per operation, not per queueMulti-AZ, not configurable
Event listenersYes — added, polled, acknowledged, nacked, full, config changedNone. Use CloudWatch metrics
Language supportJava / JVM onlySDKs for every major language
Operational modelYou run Valkey or Redis (self-hosted or managed)Fully managed, scales to zero

Ordering, Priority and Fairness

This is where the two systems differ most, and where the vocabulary is most misleading.

Reliable Queue has per-message priority. Every message carries a priority from 0 to 9, and higher-priority messages are prioritized for delivery ahead of lower-priority ones already waiting. A password-reset email jumps a backlog of marketing sends without any second queue, any routing logic, or any change to the consumer.

queue.add(QueueAddArgs.messages(
    MessageArgs.payload(passwordReset).priority(9),
    MessageArgs.payload(newsletterBatch).priority(1)
));

SQS has no equivalent, and fair queues are not one. Fair queues, added in July 2025, mitigate the noisy-neighbour problem in a multi-tenant queue: you tag each message with a MessageGroupId naming its tenant, and when one tenant floods the queue, SQS prioritises delivering messages from the quieter tenants so their dwell time stays low. Nobody is throttled; the busy tenant simply loses precedence while consumer capacity is scarce.

That is a genuinely good feature and it solves a real problem — but the problem is fairness between tenants, not urgency between messages. Fair queues cannot express "this message matters more than that one." Within a single tenant's messages, order is unchanged. And on standard queues MessageGroupId carries no ordering guarantee at all; it is purely a tenant label. If your requirement is priority, fair queues do not meet it. If your requirement is that one loud customer stops starving the others, they meet it well and Reliable Queue has no built-in answer — you would model it with priorities or separate queues.

The conventional SQS workaround for priority is multiple queues polled in order, which works, and costs you a poll against every queue on every cycle plus the logic to drain them correctly. We cover the general problem, including the starvation that naive priority schemes introduce, in distributed priority queues in Java.

Strict ordering is the third axis. SQS gives it through FIFO queues, at a real throughput cost: 300 transactions per second per API action, or 3,000 messages per second with batching. High-throughput mode lifts that a long way — to 70,000 TPS unbatched in the largest Regions, and roughly ten times that with batching — but the ceiling is tiered by Region and falls to 2,400 TPS unbatched elsewhere, so check the figure for the Region you actually deploy in rather than the headline. Reliable Queue gives ordering through ProcessingMode.SEQUENTIAL, which processes one message at a time per queue. Both make you choose between ordering and parallelism; neither escapes it.

Delivery Semantics and Failure Handling

Both systems are at-least-once by default and both provide a dead-letter queue. The difference is in what a consumer can say when processing fails.

In SQS, a consumer that fails has one move: stop touching the message and let its visibility timeout lapse, so it reappears for another consumer. ChangeMessageVisibility gives you control over when — set it to 0 and the message becomes visible immediately, extend it and the retry is deferred — so "retry this later" is expressible. What is not expressible is "this is malformed, do not retry it at all." There is no reject operation anywhere in the SQS API, and dead-letter routing is driven entirely by maxReceiveCount. A poison message therefore consumes its whole delivery budget before it reaches the DLQ, one failed attempt at a time.

Reliable Queue has explicit negative acknowledgment with two outcomes. failed redelivers the message, optionally after a delay you specify. rejected removes it and routes it straight to the dead-letter queue without burning the remaining attempts.

try {
    process(msg.getPayload());
    queue.acknowledge(QueueAckArgs.ids(msg.getId()));
} catch (TransientException e) {
    queue.negativeAcknowledge(QueueNegativeAckArgs
            .failed(msg.getId())
            .delay(Duration.ofSeconds(30)));
} catch (MalformedPayloadException e) {
    queue.negativeAcknowledge(QueueNegativeAckArgs.rejected(msg.getId()));
}

Distinguishing a transient failure from a permanent one is the difference between a retry that helps and nine retries that cannot possibly succeed. If your workload has a meaningful poison-message rate, this is the row in the table that will matter most.

Two details worth knowing before you rely on it. The delivery counter increments both when a visibility timeout lapses and when a message is negatively acknowledged as failed, so a failed nack spends an attempt — it is a retry, not a free redelivery. And add() returns null for two unrelated reasons: the message was deduplicated, or the queue is at maxSize. Same return value, very different meanings, so branch on queue depth rather than assuming a null means a duplicate.

Deduplication is the other asymmetry. SQS deduplicates only on FIFO queues, only by deduplication ID, and only within a fixed five-minute window that cannot be changed. Reliable Queue deduplicates on any queue, by ID or by payload hash, over a window you set:

queue.add(QueueAddArgs.messages(
    MessageArgs.payload(order)
               .deduplicationById(order.getId(), Duration.ofHours(24))
));

Five minutes covers a client retry. It does not cover a batch job re-run the next morning, or a webhook replayed hours later. Neither system makes your consumer idempotent for you, and you should still write it that way — but a 24-hour dedup window removes a class of duplicate that a 5-minute one cannot see.

Size, Batching and What They Cost

SQS now accepts messages up to 1 MiB, and bills every 64 KB chunk of a payload as a separate request. Neither fact is secret, and the chunk rule is not new — it has applied since 2013. But the two live on different pages, which is how the combination catches people.

PayloadBilled asEffective multiplier
Up to 64 KB1 request
256 KiB (the old maximum)4 requests
1 MiB (the new maximum)16 requests16×

The multiplier applies to each API action independently, so a message that is sent once and received once is charged twice over. Batching helps with the call count but not with the payload arithmetic: a request may carry 1 to 10 messages up to a combined 1 MiB, and the 64 KB chunking is applied to the total either way. This is also why the standard advice to keep SQS payloads small and pass a reference to S3 survives the size increase — the ceiling moved, the economics did not.

Reliable Queue has no per-message charge. Its cost is the Valkey or Redis capacity the messages occupy, which you are already paying for, plus the PRO licence. That is a genuinely different shape of bill rather than a uniformly smaller one: SQS costs nothing at zero traffic and scales linearly, while Reliable Queue has a fixed floor and near-zero marginal cost. Where the lines cross depends entirely on your volume, and we deliberately do not publish a break-even table here — cloud pricing moves quarterly and any number we printed would be wrong within two.

On metadata, SQS allows ten message attributes with string or binary values. Reliable Queue takes arbitrary headers holding any value your codec can serialise, and the payload itself is a Java object rather than a string you marshalled by hand. For messages carrying tracing context, routing keys and business metadata together, ten attributes is a ceiling teams do hit.

Timing: Visibility, Delay and Retention

Three SQS ceilings have no counterpart in Reliable Queue, and each one has a workaround that becomes part of your application.

Visibility timeout caps at 12 hours. A job that legitimately runs longer must heartbeat with ChangeMessageVisibility to hold its lease, and if the worker dies mid-heartbeat the message returns early. Reliable Queue's equivalent is visibility, set per queue or per poll, and Redisson documents no maximum for it.

Delayed delivery caps at 15 minutes. Anything further out — a reminder in three days, a retry with a long backoff, a scheduled expiry — needs EventBridge Scheduler or Step Functions alongside SQS. Reliable Queue schedules per message with millisecond precision and no documented upper bound:

queue.add(QueueAddArgs.messages(
    MessageArgs.payload(reminder).delay(Duration.ofDays(3))
));

Retention defaults to 4 days and caps at 14. The default is the part worth noticing: a queue nobody drains quietly deletes its own backlog after four days, and unless you are alarming on ApproximateAgeOfOldestMessage the loss is silent. Reliable Queue's timeToLive is off by default and set per queue or per message, so messages persist until something removes them — which is a different failure mode, not the absence of one. An undrained queue grows instead, and bounding it with maxSize is what turns that into a signal the producer can act on. We work through that in handling backpressure with Reliable Queue.

Long polling caps at 20 seconds, so an idle SQS consumer re-issues a receive call three times a minute forever, and each one is a billable request. A blocking Reliable Queue poll waits as long as you tell it to — though note that Duration.ZERO means wait forever, while omitting timeout altogether gives you a short poll that returns immediately.

The Java Side

A worker loop, both ways. SQS first:

ReceiveMessageResponse response = sqs.receiveMessage(ReceiveMessageRequest.builder()
        .queueUrl(queueUrl)
        .maxNumberOfMessages(10)
        .waitTimeSeconds(20)
        .visibilityTimeout(60)
        .build());

for (Message message : response.messages()) {
    Order order = mapper.readValue(message.body(), Order.class);
    process(order);
    sqs.deleteMessage(DeleteMessageRequest.builder()
            .queueUrl(queueUrl)
            .receiptHandle(message.receiptHandle())
            .build());
}

And Reliable Queue:

RReliableQueue<Order> queue = redisson.getReliableQueue("orders");

List<Message<Order>> batch = queue.pollMany(QueuePollArgs.defaults()
        .count(10)
        .timeout(Duration.ofSeconds(20))
        .visibility(Duration.ofSeconds(60)));

for (Message<Order> message : batch) {
    process(message.getPayload());
    queue.acknowledge(QueueAckArgs.ids(message.getId()));
}

The shapes are deliberately similar, and the differences are the ones worth noticing. There is no serialisation step, because the queue is typed and the codec is configuration rather than code. Acknowledgment is explicit rather than a delete against a receipt handle. And the failure path, shown earlier, has vocabulary that SQS does not offer.

The queue itself is configured once:

queue.setConfig(QueueConfig.defaults()
        .deliveryLimit(5)
        .visibility(Duration.ofSeconds(60))
        .timeToLive(Duration.ofDays(7))
        .deadLetterQueueName("orders-dlq")
        .maxSize(50_000)
        .processingMode(ProcessingMode.PARALLEL));

One caveat that costs people an afternoon: Reliable Queue is a Redisson PRO feature, and the interfaces ship in the open-source jar. Code written against community Redisson compiles cleanly and then throws UnsupportedOperationException from getReliableQueue() at runtime. Compiling is not evidence that you have the feature.

What You Give Up by Leaving SQS

A comparison that only lists the incumbent's limits is a brochure. Five things go the other way, and any one of them can be decisive.

You take on running a data store. SQS has no capacity to plan, no failover to test, no upgrade path, and no on-call burden. Reliable Queue inherits whatever operational posture your Valkey or Redis deployment already has. If you are already running it well, the marginal cost is close to zero. If you would be standing it up specifically for this, you have swapped a managed service for an unmanaged one and the trade is probably bad.

You lose scale-to-zero. An idle SQS queue costs nothing. An idle Valkey cluster costs whatever it costs. For bursty, low-volume background work — the case where SQS is at its strongest — this alone usually settles it.

You lose the AWS integrations. Lambda event source mappings, SNS fan-out, EventBridge targets, S3 event notifications and Step Functions all speak SQS natively. Reliable Queue is a library your application calls; anything that currently reaches your queue without going through your code will need to be rewired. The one pattern that does have a direct counterpart is SNS-to-SQS fan-out: Redisson's Reliable Fanout publishes a single message to several Reliable Queues atomically. Everything else on that list is application code you would now own.

It is Java only. SQS has an SDK in every language you might want. If your consumers are a mix of JVM services and Python workers, Reliable Queue serves half your estate. That is a hard boundary, not a roadmap gap.

Durability is opt-in, and the default is quiet. SQS replicates across Availability Zones and you cannot switch that off. Reliable Queue gives you finer control — syncMode set per operation, up to ACK_AOF, which waits for the append-only file — but syncFailureMode defaults to LOG_WARNING, meaning a replication-sync failure is recorded and swallowed rather than raised. If you are choosing Reliable Queue because of its durability controls, set THROW_EXCEPTION and handle it. The controls are better than SQS's; the defaults are more forgiving than you may want.

Memory is worth naming too, though it is less absolute than it sounds. A queue's backlog lives in RAM, so a deep backlog is a capacity question in a way it is not on SQS. In practice, if you are bounding queues with maxSize as you should, the backlog is a number you chose rather than one that surprises you.

If You Are Shopping SQS Alternatives More Broadly

Reliable Queue is the right comparison only if you are on the JVM and already run Valkey or Redis. The rest of the shortlist, honestly:

  • Amazon MQ — managed ActiveMQ or RabbitMQ. The move if you need JMS, AMQP or routing sophistication and want AWS to keep running it. More expensive than SQS and it does not scale to zero.
  • RabbitMQ — the best routing model in the category, per-message durability, quorum queues for replicated durability. The trade is operating an Erlang cluster. See Redis vs RabbitMQ.
  • Kafka or MSK — the answer only if you need a replayable log rather than a queue. Share groups in Kafka 4.2 narrowed the queueing gap, but you are still operating a partitioned log to get it. See Apache Kafka alternatives and Redis vs Kafka.
  • Google Cloud Pub/Sub or Azure Service Bus — the same managed trade as SQS with a different vendor. Service Bus schedules messages at any future time rather than capping at 15 minutes, and its sessions give ordered, grouped processing. It has no per-message priority either, though: Microsoft's own guidance for that is the same separate-queue pattern SQS users reach for.
  • Cloudflare Queues, or an SQS-compatible self-hosted broker — the usual answers when the driver is cost or getting off AWS rather than a missing capability.
  • Redis Streams — already available in open-source Valkey and Redis, with consumer groups and explicit acknowledgment. No delivery limits, dead-lettering, priority or delayed delivery, so those become your application's problem. See Redis Streams for Java.

If your queue is really JMS, the comparison to make is a different one — JMS messaging over Valkey and Redis covers TCK-certified Jakarta Messaging 3.1 on the same infrastructure.

How to Choose

Stay on SQS if your volume is bursty or low, if scale-to-zero matters, if consumers are polyglot, if your pipeline is wired through Lambda, SNS or EventBridge, or if nobody on the team wants to own a data store. This is most teams, and it is not a compromise.

Consider Reliable Queue if you are on the JVM, already run Valkey or Redis in production, and are working around a specific SQS limit: you need per-message priority, or negative acknowledgments, or scheduling beyond 15 minutes, or a deduplication window longer than five minutes, or a visibility lease longer than 12 hours, or backpressure on queue depth — or your per-request bill has become a line item someone is asking about.

The earlier version of this page concluded that Reliable Queue was "the clear winner." That was overstated. It wins decisively on delivery semantics and on the ceilings above, and it loses just as decisively on operational burden, ecosystem and language coverage. Which set of properties you are buying is the whole question.

Frequently Asked Questions

What Is the Maximum SQS Message Size?

1 MiB (1,048,576 bytes), raised from 256 KiB in August 2025. Larger payloads up to 2 GB are possible with the Amazon SQS Extended Client Library, which stores the body in S3 and sends a reference. Note that SQS bills each 64 KB chunk of a payload as a separate request, so a 1 MiB message is billed as 16 requests per API action.

Does Amazon SQS Support Message Priority?

No. SQS has no per-message priority on either standard or FIFO queues. Fair queues, added in July 2025, are often mistaken for priority but do something different: they reduce dwell time for quiet tenants when one tenant floods a multi-tenant queue. They cannot express that one message is more urgent than another. The usual workaround is separate queues per priority level, polled in order. Redisson PRO Reliable Queue supports per-message priority from 0 to 9.

What Are SQS Fair Queues?

Fair queues mitigate the noisy-neighbour problem on multi-tenant standard queues. You tag messages with a MessageGroupId identifying the tenant, and when one tenant has disproportionately many messages in flight, SQS prioritises delivering other tenants' messages so their dwell time stays low. No tenant is throttled, no consumer changes are needed, and it applies automatically to standard queues carrying a group ID. On standard queues the group ID carries no ordering guarantee — it is only a tenant label.

Is Amazon SQS Like RabbitMQ?

They solve the same problem differently. SQS is a fully managed queue with no routing layer: producers send to a queue and consumers poll it. RabbitMQ is a broker you operate, where producers publish to exchanges and bindings decide which queues receive each message — giving direct, topic, fanout and header routing that SQS has no equivalent for. SQS wins on operational burden; RabbitMQ wins on routing sophistication and per-message control.

Can Redis Replace Amazon SQS?

For a JVM application already running Valkey or Redis, yes — Redisson PRO's Reliable Queue provides acknowledgments, visibility timeouts, delivery limits, dead-letter queues, priority and delayed delivery, which is the feature surface most teams actually use SQS for. It is not a replacement if you need polyglot clients, scale-to-zero economics, or native integration with Lambda, SNS and EventBridge. Raw Redis lists and Redis Streams are a weaker substitute, since neither provides delivery limits, dead-lettering or priority without application code.

How Long Can an SQS Message Be Delayed?

15 minutes, per message or as a queue-wide default. Anything longer requires EventBridge Scheduler, Step Functions or a scheduling layer of your own. Reliable Queue schedules per message with millisecond precision and no documented upper bound.

How Long Does SQS Keep Messages?

Four days by default, configurable from 60 seconds to 14 days. The default catches people out: an undrained queue deletes its own backlog after four days, and the loss is silent unless you are alarming on the ApproximateAgeOfOldestMessage metric.

Is Reliable Queue Available in the Open-Source Edition?

No, it is a Redisson PRO feature. The interfaces ship in the open-source jar, so code compiles against community Redisson and then throws UnsupportedOperationException from getReliableQueue() at runtime. Compiling successfully is not evidence that you have the feature.

Does Reliable Queue Work with Valkey?

Yes. Valkey is wire-compatible with Redis and every example on this page runs unchanged against either, self-hosted or on any managed provider.

Next Steps

All about Reliable Queue walks through acknowledgments, delivery limits and dead-letter routing in detail, and handling backpressure covers bounding a queue with maxSize. For the concepts behind the comparison, see what a message queue is and using Redis as a queue. Full configuration is in the Redisson queues documentation.

The Reliable Queue, along with local caching, data partitioning and advanced eviction, is part of Redisson PRO. You can try it for free.