A dead-letter queue (DLQ) is a holding area for messages that couldn't be processed or delivered after a set number of attempts. Instead of retrying a failing message forever, or silently dropping it, the system moves it aside to the DLQ. The main queue keeps flowing, and the failed message stays available for someone to inspect, fix and replay.
{
"RedrivePolicy": {
"deadLetterTargetArn": "arn:aws:sqs:eu-west-1:123456789012:orders-dlq",
"maxReceiveCount": "5"
}
}
This Amazon SQS policy moves any message that's been received five times without being deleted into the orders-dlq queue.
How a dead-letter queue works
- A consumer receives a message from the main queue and tries to process it.
- Processing fails: the consumer crashes, throws, or doesn't acknowledge in time.
- The message becomes visible again and is retried, often with exponential backoff.
- After the maximum attempts (or a time limit), the broker moves it to the DLQ.
- An alert fires. Someone inspects the message, fixes the cause, and redrives it back to the main queue or replays it to the destination.
SQS, Google Pub/Sub, Azure Service Bus, RabbitMQ (via dead-letter exchanges) and most job frameworks support this pattern. With Kafka, it's usually implemented as a separate topic that a consumer writes failed records to.
Why you want one
- No poison messages blocking the queue. One malformed message that always crashes the consumer can stall everything behind it, especially with ordered queues.
- No silent loss. Without a DLQ, a message that runs out of retries is just gone.
- A clear signal. DLQ depth is one of the most useful alerts you can have. A non-empty DLQ means something needs attention.
- Recovery after outages. When a downstream service is down longer than the retry window, its messages wait in the DLQ until it's back.
Dead-letter queues and webhooks
For webhooks, the "queue" is the sender's delivery pipeline and the consumer is your endpoint. When every retry fails, the delivery has the same problem a dead message has: it has to go somewhere or be lost.
As a webhook sender, keep failed deliveries in a failed state or DLQ with the last response, show them to the customer, and offer a way to replay them once the endpoint is fixed. Disable endpoints that fail for a long time instead of retrying them forever.
As a webhook receiver, you don't control the provider's DLQ, and many providers don't have one: after their last retry, the event is dropped. Two common patterns protect you:
- Accept, then queue. Your endpoint writes each verified webhook to your own queue and returns
200. Processing failures land in your DLQ, where you can replay them. See webhook to SQS. - Put a gateway in front. A webhook gateway stores every request before delivery and retries on its own schedule, so an endpoint outage doesn't depend on the provider's retry policy.
How Webhook Relay handles failed deliveries
Webhook Relay doesn't call it a DLQ, but failed deliveries are kept and recoverable, which serves the same purpose:
- Inbound webhooks. Each webhook is stored in the bucket's logs with every delivery attempt. With durable retries, failing deliveries are shown as
stalledand retried with backoff for up to 30 days. From the log you can resend any delivery, recover failed deliveries in bulk after an endpoint recovers, or replay missing events. - Outbound webhooks. In the Outbound Gateway, a delivery that runs out of retries (up to 8 attempts within 48 hours) is marked
failedand kept for 35 days. Once the customer's endpoint is fixed, a recover call re-sends that endpoint's failed deliveries with the originalwebhook-id. See monitor, retry and recover deliveries.
Common mistakes
- No alert on the DLQ. A DLQ nobody watches is a slower way of losing messages.
- DLQ retention shorter than your response time. SQS keeps messages for up to 14 days. Make sure the retention covers a long weekend.
- Redriving before fixing the cause. The messages fail again and go straight back.
- Non-idempotent consumers. A message in the DLQ may have been partly processed. Replaying it must be safe. See idempotency.
- Retrying errors that will never succeed. Send obviously invalid messages, such as unparseable payloads, to the DLQ immediately instead of burning retries on them.
