← All one-liners·#030·ops·aws·expert

SQS: replay DLQ messages back to source

The 'I deployed a fix, now reprocess every failure' one-liner. Drains a dead-letter queue and re-sends each body to the source queue.

Setup
  • → brew install awscli
  • → aws configure
Cost per run
free
The one-liner
$ DLQ="https://sqs.eu-west-1.amazonaws.com/123/my-dlq"
SRC="https://sqs.eu-west-1.amazonaws.com/123/my-queue"

while MSGS=$(aws sqs receive-message --queue-url "$DLQ" \
               --max-number-of-messages 10 --wait-time-seconds 5) \
  && echo "$MSGS" | jq -e '.Messages | length > 0' >/dev/null; do
  echo "$MSGS" | jq -c '.Messages[]?' | while read -r M; do
    BODY=$(echo "$M" | jq -r .Body)
    RH=$(echo "$M" | jq -r .ReceiptHandle)
    aws sqs send-message   --queue-url "$SRC" --message-body "$BODY" >/dev/null \
      && aws sqs delete-message --queue-url "$DLQ" --receipt-handle "$RH" >/dev/null
  done
done
What each stage does
  1. [01] awswhile … receive-message … do … done
    Loop until the DLQ returns no messages. Long-poll with 5s wait keeps SQS API calls cheap.
  2. [02] jqjq -c '.Messages[]?'
    Emit each message as a single-line JSON object — fed into the inner `while read` loop.
  3. [03] awsaws sqs send-message --queue-url "$SRC" --message-body "$BODY"
    Re-publish the original body to the source queue. Original message attributes are dropped — add `--message-attributes` from the message if your consumer needs them.
  4. [04] awsaws sqs delete-message --queue-url "$DLQ" --receipt-handle "$RH"
    Only delete from DLQ AFTER successful send (the `&&` enforces this). Loses zero messages even on partial failure.
Expected output (sample)
(silent — successful replays produce no stdout)

Verify with: aws sqs get-queue-attributes --queue-url "$DLQ" --attribute-names ApproximateNumberOfMessages
Caveats & tips
  • Test on a non-production DLQ first. Replay loops are easy to get wrong.
  • If your DLQ has thousands of messages, run multiple workers in parallel — each receive-message only gets up to 10.
  • Add `sleep 0.1` inside the inner loop if you hit SQS API throttling.