When to Use Amazon SQS FIFO Queues: Ordering, Exactly‑Once, and Operational Guidance
Choosing FIFO over standard SQS queues hinges on per‑entity ordering and deduplication needs. This architecture note explains requirements, minimal design, trust boundaries, operational checks, and when to switch.
08 Jan 2026, 11:14 UTC

Why the Decision Matters
When a system must process messages in strict order for a given entity—such as a customer, order, or device—Amazon SQS FIFO queues provide the only AWS‑managed service that guarantees per‑group ordering and exactly‑once delivery semantics. If ordering is not required, the cheaper and higher‑throughput Standard queue is usually the better fit. This note walks through the exact conditions that justify FIFO, the smallest design that satisfies those conditions, and the operational checks you need to keep the system healthy.
Requirements that Force FIFO
- Per‑Entity Ordering: All messages that belong to the same logical “entity” (e.g., a single order) must be processed in the order they were sent.
- Exactly‑Once Delivery: The queue must deduplicate messages that arrive within a 5‑minute window. This can be content‑based (hash of the body) or explicit via
MessageDeduplicationId. - Throughput Limits: FIFO queues support 300 messages per second (SQS API action) without batching and 3,000 with batching. High‑throughput mode allows up to 3,000 messages per second per
MessageGroupIdbut the overall queue still respects the 300‑msg/s limit.
The Minimal Design
For most workloads that need ordering, the smallest viable design is:
- Create one FIFO queue per ordered entity stream.
- Use
MessageGroupIdto shard the ordering scope. For example,MessageGroupId = "customer-{{customerId}}"ororder-{{orderId}}. - Enable
ContentBasedDeduplicationunless producers can supply a stableMessageDeduplicationIdthat never collides across different entities. - Attach a Dead‑Letter Queue (DLQ) with a
maxReceiveCountthat matches your retry policy.
Example CLI snippet to create the queue:
aws sqs create-queue \
--queue-name Orders.fifo \
--attributes \
FifoQueue=true, \
ContentBasedDeduplication=true, \
VisibilityTimeout=30, \
RedrivePolicy='{"maxReceiveCount":"5", "deadLetterTargetArn":"arn:aws:sqs:us-east-1:123456789012:OrdersDLQ"}'
Replace placeholders with your account, region, and DLQ ARN.
Trust Boundary & Deduplication
Producers are considered untrusted for deduplication correctness. If a producer can reliably generate a stable MessageDeduplicationId that is unique per logical message, you can disable content‑based deduplication. However, any collision across distinct messages will silently drop the second message, breaking exactly‑once guarantees. The safest practice is to let SQS compute the hash of the body.
Operational Checks
- ApproximateNumberOfMessagesVisible: Monitors how many messages are ready to be processed.
- ApproximateAgeOfOldestMessage: Alarm if this exceeds your ordering SLA (e.g., 2 minutes). This metric is per queue, not per group, so it’s a coarse indicator.
- DLQ Depth: A rising DLQ size indicates poison messages or consumer failures. Set an alarm on
ApproximateNumberOfMessagesVisiblefor the DLQ. - Consumer Error Rates: Log the number of
ReceiveMessagefailures and failedDeleteMessagecalls. A high failure rate may mean the consumer is not idempotent or the queue is overloaded.
Failure Modes & Mitigations
Poison Message: If a message at the head of a MessageGroupId fails repeatedly, all subsequent messages for that group are blocked. The DLQ will eventually receive the poison message after maxReceiveCount retries. Mitigation: design consumers to be idempotent and quickly fail on corrupt payloads, and consider a maxReceiveCount of 5–10 depending on your tolerance for blocking.
Throughput Saturation: If you hit the 300‑msg/s limit, you’ll see increased ApproximateAgeOfOldestMessage and consumer backlogs. Mitigation: split the workload into multiple FIFO queues or switch to a Standard queue if ordering is not strictly required.
When to Switch to Standard
- Throughput exceeds FIFO limits and ordering can be relaxed per entity.
- Consumers are idempotent and can handle duplicate deliveries; you can perform deduplication at the consumer side.
- Ordering is only needed within a partition key that is already enforced elsewhere (e.g., Kinesis shard).
Comparison Table
| Feature | FIFO Queue | Standard Queue |
|---|---|---|
| Ordering | Per‑MessageGroupId (exact) | No ordering guarantee |
| Exactly‑Once Delivery | Yes (within 5‑min dedup window) | No; duplicates possible |
| Throughput (per API action) | 300 msg/s (no batch) / 3,000 (batch) | Up to 120,000 msg/s (batch) |
| High‑Throughput Mode | Enabled per MessageGroupId (3,000 msg/s) | Not applicable |
| Cost | ~1.5× higher per message | Cheaper |
Concrete Diagnostic Scenario
Suppose you notice that messages for order-123 are arriving out of order. First, verify that all producers use the same MessageGroupId value. Next, check the ApproximateAgeOfOldestMessage for the queue; a high value may indicate a poison message. Pull a few messages via the console or CLI and confirm the order:
aws sqs receive-message \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/Orders.fifo \
--max-number-of-messages 10 \
--message-attribute-names All
Inspect the MessageId and ApproximateFirstReceiveTimestamp fields to ensure they match the send order. If duplicates appear, confirm that ContentBasedDeduplication is enabled and that the message bodies are identical. If you see duplicates that should be distinct, producers are likely reusing the same MessageDeduplicationId across different entities; switch to content‑based or generate unique IDs.
Practical Checklists
- Confirm
FifoQueue=truein queue attributes. - Verify
ContentBasedDeduplication=trueunless custom dedup IDs are guaranteed unique. - Ensure
RedrivePolicypoints to a DLQ withmaxReceiveCount<= 5. - Set CloudWatch alarms on
ApproximateAgeOfOldestMessageand DLQ depth. - Run a test with two producers sending the same body to the same
MessageGroupIdand confirm only one message is received.
Conclusion
Use FIFO queues when you need strict ordering per logical entity and you can tolerate the throughput and cost trade‑offs. Design with a single queue per entity stream, rely on content‑based deduplication, and attach a DLQ. Monitor the key metrics and be ready to split the queue or switch to Standard if throughput demands grow or ordering can be relaxed. By following this minimal design and operational guardrails, you can achieve reliable, exactly‑once processing without reinventing ordering logic in your application.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.