What Is SQS?
SQS is the simplest messaging service on AWS. A producer puts messages on a queue. A consumer reads messages, processes them, and deletes them. If processing fails, the message becomes visible again after a timeout and gets retried.
That's it. No routing, no topics, no filtering. Just reliable delivery with retries.
Standard vs FIFO
| Standard | FIFO | |
|---|---|---|
| Throughput | Unlimited | 300 msg/s (3,000 with batching) |
| Ordering | Best-effort (usually in order, not guaranteed) | Strict within message group |
| Delivery | At-least-once (possible duplicates) | Exactly-once |
| Cost | $0.40/million requests | $0.50/million requests |
| Use case | High-volume processing, fan-out | Financial transactions, ordered workflows |
Default to Standard. Design your consumers to be idempotent (processing the same message twice is safe), and you get unlimited throughput. FIFO is for when ordering within a group genuinely matters and you can't tolerate duplicates.
Dead-Letter Queues
When a message fails processing repeatedly, it moves to a dead-letter queue (DLQ) instead of retrying forever:
Main Queue (maxReceiveCount: 3)
β Success: message deleted
β Failure: retried up to 3 times
β After 3 failures: moved to DLQ
The DLQ is just another SQS queue. You need a strategy for what happens next:
- Manual investigation (look at DLQ messages, fix the bug, redrive)
- Automated redrive (after deploying a fix, push DLQ messages back to the main queue)
- Alerting (alarm when DLQ depth > 0)
AWS now supports native redrive from the console and API. No manual copy/delete loops.
SQS vs SNS vs EventBridge
These three get confused constantly:
| SQS | SNS | EventBridge | |
|---|---|---|---|
| Pattern | Point-to-point queue | Pub/sub fan-out | Event bus with content routing |
| Consumers | One consumer group | Multiple subscribers | Multiple targets per rule |
| Filtering | None (consumer gets everything) | Attribute-based filtering | Content-based pattern matching |
| Ordering | FIFO only | FIFO only | No ordering guarantee |
| Retry | Automatic (visibility timeout) | Immediate retry, then DLQ | Rule-level DLQ |
| Best for | Work queues, backpressure | Notifications, fan-out | Decoupled microservices |
Common combination: EventBridge β SQS. EventBridge routes events to the right queue; SQS handles reliable processing with backpressure and retries.
Another common pattern: SNS β SQS fan-out. One SNS topic with multiple SQS subscriptions. Each queue processes independently at its own pace.
Scaling Patterns
Lambda consumer
Lambda polls SQS automatically. Scales from 0 to your concurrency limit based on queue depth. The simplest pattern for serverless architectures.
Key settings:
- Batch size: How many messages per Lambda invocation (1-10,000)
- Batch window: Wait up to N seconds to fill a batch (reduces invocations)
- Concurrency: Lambda scales up to 1,000 concurrent executions (configurable)
- Report batch item failures: Only retry failed messages, not the whole batch
ECS/EC2 consumer
Long-poll from the queue in a loop. Scale the number of tasks/instances based on queue depth (CloudWatch metric: ApproximateNumberOfMessagesVisible).
Auto-scaling based on queue depth
Target: Keep messages-per-consumer below a threshold
Metric: ApproximateNumberOfMessagesVisible / DesiredCount
Target value: 10 (each instance processes 10 messages at a time)
Visibility Timeout
When a consumer reads a message, it becomes invisible to other consumers for the visibility timeout period. If the consumer doesn't delete it within that window, the message becomes visible again (and another consumer picks it up).
Set visibility timeout > your processing time. If your Lambda timeout is 60 seconds, set visibility timeout to at least 60 seconds.
CDK Example
import { Queue, DeadLetterQueue } from 'aws-cdk-lib/aws-sqs';
const dlq = new Queue(this, 'OrderDLQ', {
queueName: 'order-processing-dlq',
retentionPeriod: Duration.days(14),
});
const queue = new Queue(this, 'OrderQueue', {
queueName: 'order-processing',
visibilityTimeout: Duration.seconds(60),
deadLetterQueue: {
queue: dlq,
maxReceiveCount: 3,
},
});
// FIFO variant
const fifoQueue = new Queue(this, 'PaymentQueue', {
queueName: 'payment-processing.fifo',
fifo: true,
contentBasedDeduplication: true,
visibilityTimeout: Duration.seconds(30),
});
Cost Considerations
- $0.40 per million requests (Standard), $0.50 per million (FIFO)
- First 1 million requests/month are free
- Long polling (
WaitTimeSeconds: 20) reduces empty responses (and cost) - Batch operations count as one request (send/receive/delete up to 10 messages per request)
SQS is extremely cheap. A queue processing 10 million messages/month costs ~$4. The real cost is usually the compute (Lambda, ECS) consuming the messages, not the queue itself.
Further Reading
- SQS Developer Guide
- SQS FIFO queues
- Using Lambda with SQS
- SQS pricing
- Using SQS with .NET: Lambda consumers,
IHostedServicepattern, and typed messages in C#
Looking for hands-on help? View my AWS architecture services β