SQS in front of Lambda is the most boring, reliable pattern in my stack. Its only pain is configuration, and the three fixes above cover nearly every DLQ surprise I've seen.
Use it when
- Anything that should survive spikes and failures between producers and Lambda.
- Batched ingestion (IoT, webhooks) into Postgres.
Don't use it when
- Multi-step jobs where a failure in step 3 shouldn't rerun steps 1 and 2: look at Lambda durable functions.
Pricing
Sydney: $0.40 per million requests (Standard), $0.50 (FIFO). A request carries up to 10 messages, so batching cuts the bill tenfold: 50M messages ≈ $6 batched, $60 unbatched.
In my words: what hurts
- Retry and timeout settings have to be exactly right, or messages land in the DLQ.
Gotchas, with fixes
Gotcha: Messages reappear and get processed twice, or go to the DLQ, when the queue's visibility timeout is too close to the function's timeout.
Fix: Set the visibility timeout to at least six times the function timeout.
Gotcha: One bad message in a batch fails the whole batch, and the good messages retry too.
Fix: Enable ReportBatchItemFailures and return only the failed message IDs.
Gotcha: Messages bounce to the DLQ after too few attempts, or loop forever.
Fix: Set maxReceiveCount deliberately (for example 3–5), and alarm on DLQ depth.
Provisioning
Official Pulumi package · Official Terraform provider
@pulumi/aws sqs.Queue + lambda.EventSourceMapping.
Instead, consider
- Lambda durable functions Multi-step jobs with flaky dependencies.
- Cloudflare Queues Only when both ends are on Workers.
Worked examples
Where it fits
- Background jobs and retries: Pick 01.
The queue. Set visibility timeout to six times the function timeout.
- Event-driven plumbing: Pick 02.
Work: buffer, retry, dead-letter, and let Lambda take messages in batches.
- Device and IoT data: Pick 02.
Buffer, then batch into Lambda.
Sources
Last reviewed by Nicolas Dao