Schedule icon
Consume icon
If icon
Script icon
Docker icon
Publish icon
Log icon
Upload icon
SlackIncomingWebhook icon

RabbitMQ DLQ Poison-Pill Quarantine and Safe Replay Gate

Drain RabbitMQ DLQs with Kestra, classify root causes, safely replay transient failures back to the exchange, and isolate poison pills into Amazon S3.

Categories
CloudDataInfrastructure

Dead-Letter Queues (DLQs) in RabbitMQ capture unprocessable messages rejected by downstream consumers or expired by queue message TTLs. Left unattended, DLQs turn into unmonitored data graveyards that consume broker memory and conceal upstream schema regressions. When operators attempt manual replays without root-cause triage, unhandled poison pills immediately crash restarted consumer workers again.

This blueprint implements an automated Dead-Letter Queue remediation and quarantine pipeline. It periodically drains dead-lettered messages using io.kestra.plugin.amqp.Consume, analyzes AMQP headers (x-death, delivery attempts, error stack traces), classifies events into transient errors versus toxic poison pills, safely replays recoverable records back to the primary exchange with updated metadata, and isolates unrecoverable records to Amazon S3 cold storage with structured forensic manifests while alerting on-call engineers via Slack.

How it works

  1. periodic_dlq_drain (io.kestra.plugin.core.trigger.Schedule) executes every 15 minutes. Shipped disabled so you can test in safe mode first.
  2. drain_dlq (io.kestra.plugin.amqp.Consume) pulls up to max_records from dlq_queue into Kestra internal storage.
  3. evaluate_drained_messages (io.kestra.plugin.core.flow.If) branches based on whether any messages were drained. If the queue is empty, log_clean_dlq records a healthy state and completes.
  4. classify_and_triage (io.kestra.plugin.scripts.python.Script) inspects the downloaded batch:
    • Extracts x-death array, custom retry headers, and payload structures.
    • Identifies TRANSIENT_RECOVERABLE messages (network timeouts, HTTP 503/504, rate limits) where retry_count < max_retry_attempts.
    • Identifies POISON_CORRUPT_PAYLOAD messages (invalid JSON, syntax errors, missing fields).
    • Identifies RETRY_BUDGET_EXHAUSTED messages that have exceeded max_retry_attempts.
    • Generates quarantine_manifest.json, replay_batch.json, and summary metrics.
  5. evaluate_replay (io.kestra.plugin.core.flow.If) gates replay behind inputs.dry_run == 'false'. When live, replay_to_exchange (io.kestra.plugin.amqp.Publish) republishes recoverable messages back to target_exchange with routing key target_routing_key.
  6. evaluate_quarantine (io.kestra.plugin.core.flow.If) uploads the forensic manifest to Amazon S3 via io.kestra.plugin.aws.s3.Upload and notifies Slack with direct S3 links.
  7. notify_triage_summary posts an operational recap to Slack detailing total drained, replayed, and quarantined counts.
  8. The flow-level errors block alerts Slack immediately if broker connectivity or storage operations fail.

What you get

  • Automated DLQ drainage preventing broker memory bloat and silent message accumulation.
  • Automated failure taxonomy separating transient network blips from unrecoverable poison pills.
  • Safe, gated message replay protected by an explicit dry_run switch and maximum retry budget.
  • Full audit compliance with forensic JSON manifests archived in partitioned S3 storage.
  • Real-time Slack notifications for triage recaps and urgent poison-pill alerts.

Who it's for

  • Platform and SRE teams running RabbitMQ in production microservices architectures.
  • Data and backend engineers maintaining event-driven messaging pipelines and order processing queues.
  • Anyone managing RabbitMQ who needs an automated DLQ consumer and replay mechanism instead of running manual CLI scripts.

Why orchestrate this with Kestra

RabbitMQ does not natively triage, inspect, or selectively replay messages from DLQs. A standalone Python script running on a cron lacks execution lineage, secret management, persistent audit artifacts, and native multi-cloud integrations. Kestra delivers declarative AMQP consumption and publication, Docker sandboxing for payload inspection, native S3 archiving, conditional flow control, and end-to-end execution history in a single observable YAML flow.

Prerequisites

  • A running RabbitMQ broker with an existing Dead-Letter Queue (e.g. orders.dlq) and a target exchange (e.g. orders.exchange).
  • An Amazon S3 bucket configured for cold-storage quarantine archives.
  • An incoming Slack webhook for incident alerts and triage summaries.
  • Docker available to the Kestra Worker for running Python payload inspection.

Secrets

  • RABBITMQ_HOST: RabbitMQ broker hostname or IP address.
  • RABBITMQ_PORT: AMQP port (typically 5672).
  • RABBITMQ_USERNAME: Broker username.
  • RABBITMQ_PASSWORD: Broker password.
  • RABBITMQ_VHOST: RabbitMQ virtual host (default /).
  • AWS_ACCESS_KEY_ID: IAM access key with s3:PutObject permissions.
  • AWS_SECRET_ACCESS_KEY: IAM secret key matching the access key.
  • SLACK_WEBHOOK_URL: Slack Incoming Webhook URL.

Inputs

Name Type Default Description
dlq_queue STRING orders.dlq Name of the RabbitMQ Dead-Letter Queue to drain
target_exchange STRING orders.exchange Destination exchange for recoverable message replay
target_routing_key STRING orders.created Routing key applied to replayed messages
max_records INT 50 Maximum messages drained per execution cycle
max_retry_attempts INT 3 Maximum retry budget before permanent quarantine
dry_run SELECT "true" When true, simulate triage without publishing to exchange
s3_quarantine_bucket STRING company-dlq-quarantine-archive Amazon S3 bucket for forensic quarantine archives
s3_region STRING us-east-1 AWS region where quarantine bucket resides

Outputs

  • outputs.drain_dlq.count: Total number of messages drained from the DLQ.
  • outputs.classify_and_triage.vars.replay_count: Number of transient messages identified for replay.
  • outputs.classify_and_triage.vars.quarantine_count: Number of toxic messages archived to S3.
  • outputs.classify_and_triage.outputFiles['quarantine_manifest.json']: Forensic JSON manifest file URI.

Quick start

  1. Add the RabbitMQ, AWS, and Slack secrets to your Kestra namespace.
  2. Run the flow manually with dry_run: "true" to inspect your DLQ backlog safely.
  3. Review the logs and generated triage_summary.json to confirm failure classifications.
  4. Set dry_run: "false" on the next execution to replay verified transient messages to your target exchange.
  5. Enable periodic_dlq_drain to automate scheduled queue maintenance.

How to extend

  • Add custom regex patterns to the Python script to classify specific microservice error codes.
  • Route quarantined messages to an Elasticsearch or OpenSearch index for full-text search across stack traces.
  • Chain a downstream subflow using io.kestra.plugin.core.flow.Subflow to automatically open Jira tickets for unhandled poison pills.

Pitfalls

  • Replay Loops: Replaying poison pills without incrementing retry headers or filtering out corrupt payloads causes endless consumer crash loops. This blueprint stops loops by enforcing max_retry_attempts and strict payload validation.
  • Live Replay in Production: Always execute initial runs with dry_run: "true" to inspect payload classification before enabling live republishing.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.