Home/Learn/RabbitMQ/Dead Letter Queues (DLQ)

Dead Letter Queues (DLQ)

Intermediate
Dead-Lettering

Messages that cannot be processed land in a DLQ; monitor DLQ depth as an SLA signal and build tooling to replay, inspect, or discard messages after investigation.

Overview

A Dead Letter Queue (DLQ) is a holding area for messages that could not be delivered or processed. In RabbitMQ, messages are dead-lettered (moved via the Dead Letter Exchange) when they are: **rejected** (`basicNack`/`basicReject` with `requeue=false`), **expired** (exceeded `x-message-ttl` or per-message TTL), or **maxlen-exceeded** (queue is full and `x-overflow=reject-publish-dlx`). A DLQ is not automatically created — you must set `x-dead-letter-exchange` on the source queue. DLQ depth is a critical SLA metric: a growing DLQ means your consumer is failing; an empty DLQ is the goal. Build tooling to **inspect** (view message content), **replay** (re-publish to original exchange), and **discard** (drop definitively) DLQ messages.

Configuring a DLQ with x-dead-letter-exchange

Declare a DLX (Dead Letter Exchange) and a DLQ, then bind the DLQ to the DLX. Set `x-dead-letter-exchange` on the source queue to route rejected/expired messages there. Optionally set `x-dead-letter-routing-key` to override the routing key — useful when the DLX is a direct exchange.

Spring AMQP — DLX and DLQ configuration
@Configuration
class DlqConfig {

    // DLX — a direct exchange that routes to the DLQ
    @Bean
    DirectExchange deadLetterExchange() {
        return new DirectExchange("orders.dlx");
    }

    // DLQ — the queue that holds dead-lettered messages
    @Bean
    Queue deadLetterQueue() {
        return QueueBuilder.durable("orders.dlq").build();
    }

    @Bean
    Binding dlqBinding() {
        return BindingBuilder.bind(deadLetterQueue())
            .to(deadLetterExchange())
            .with("orders.dead");   // routing key
    }

    // Source queue — messages here get dead-lettered to DLX
    @Bean
    Queue ordersQueue() {
        return QueueBuilder.durable("orders.processing")
            .withArgument("x-dead-letter-exchange",    "orders.dlx")
            .withArgument("x-dead-letter-routing-key", "orders.dead")
            .withArgument("x-message-ttl",             300_000)   // 5min TTL
            .build();
    }
}

Dead-Lettering in Consumer Code

Send a message to the DLQ by calling `basicNack(tag, false, false)` — the third parameter `requeue=false` tells RabbitMQ not to re-queue the message; the DLX routing takes over. In Spring AMQP, this happens automatically when a `@RabbitListener` throws and the `SimpleRabbitListenerContainerFactory` exhausts its retry attempts (configured via `RetryInterceptorBuilder`).

Spring AMQP — manual and automatic dead-lettering
@RabbitListener(queues = "orders.processing")
public void processOrder(Order order, Channel channel,
                         @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
    try {
        orderService.process(order);
        channel.basicAck(tag, false);           // success
    } catch (PoisonMessageException e) {
        // Unrecoverable: send straight to DLQ, never retry
        log.error("Poison message: {}", order.getId(), e);
        channel.basicNack(tag, false, false);   // requeue=false → DLX → DLQ
    } catch (TransientException e) {
        // Recoverable: requeue for immediate retry
        channel.basicNack(tag, false, true);    // requeue=true → back in queue
    }
}

// Automatic DLQ routing via Spring Retry + RejectAndDontRequeueRecoverer
factory.setAdviceChain(RetryInterceptorBuilder.stateless()
    .maxAttempts(3)
    .backOffOptions(1_000, 2.0, 10_000)
    .recoverer(new RejectAndDontRequeueRecoverer())  // → DLX after 3 attempts
    .build());

Monitoring and Replaying DLQ Messages

A growing DLQ is a P1 incident signal. Alert on `rabbitmq_queue_messages{queue="orders.dlq"} > 0`. For replay, re-publish DLQ messages to the original exchange. The `x-death` header on dead-lettered messages contains the reason, count, and queue name — invaluable for debugging. Build a replay endpoint or use the RabbitMQ Management UI.

RabbitMQ — DLQ monitoring and replay
// Prometheus alert — DLQ not empty
- alert: RabbitMQDlqNonEmpty
  expr: rabbitmq_queue_messages{queue=~".*dlq"} > 0
  for: 1m
  annotations:
    severity: warning
    summary: "RabbitMQ DLQ has {{ $value }} messages — investigate consumer errors"

// Replay: consume from DLQ and re-publish to original exchange
@PostMapping("/admin/dlq/replay")
void replayDlq(@RequestParam int count) {
    for (int i = 0; i < count; i++) {
        Message msg = rabbitTemplate.receive("orders.dlq", 1_000);
        if (msg == null) break;
        Map<?,?> xDeath = (Map<?,?>) msg.getMessageProperties()
            .getHeaders().get("x-death");
        String originalExchange = (String) ((List<?>) xDeath).get(0) ...;
        // Re-publish to the original exchange
        rabbitTemplate.send("orders.topic", "order.created", msg);
    }
}

Key Points to Remember

  • 1Messages are dead-lettered on: basicNack(requeue=false), TTL expiry, or queue length overflow
  • 2DLX + DLQ must be explicitly configured — set x-dead-letter-exchange on the source queue
  • 3x-death header on dead-lettered messages contains reason, count, and origin queue
  • 4DLQ depth is a critical SLA metric — alert immediately when DLQ becomes non-empty
  • 5RejectAndDontRequeueRecoverer + Spring Retry auto-routes to DLQ after exhausting retries
  • 6Build replay tooling: re-publish DLQ messages to the original exchange for re-processing

Interview Questions

Sign in to ask Aria
1

What are the three reasons a message gets dead-lettered in RabbitMQ?

EasyBooking.com
2

What is the difference between requeue=true and requeue=false in basicNack?

EasyAmazon
3

How would you replay messages from a DLQ back to the original queue?

MediumZalando
4

What information does the x-death header contain and how would you use it?

MediumThoughtWorks
5

How would you distinguish a transient failure (should retry) from a poison message (should DLQ immediately)?

HardDeliveroo

Ask Aria about Dead Letter Queues (DLQ)

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…