See what actually died
Peek at messages without consuming them. Pretty JSON, headers, and the complete x-death history: which queue rejected it, how often, when.
The management UI configures your broker and tells you a queue holds 300 messages. Warren runs the traffic on it: what those messages are, why they died, who should have consumed them, and one audited click to put them back.
Self-hosted next to your broker. Works with RabbitMQ 3.x and 4.x, classic and quorum queues. The Community edition ships first; the waitlist gets the image on release day.
| Queue | Messages | Consumers | Dead-letters to |
|---|---|---|---|
| orders.dlqDLQ | 9 | 0 | – |
| billing.invoice.deadDLQ | 5 | 0 | – |
| orders.process | 3 | 4 | orders.dlx |
| shipping.label | 7 | 2 | – |
| Message id | Last death | Original route | Payload | |
|---|---|---|---|---|
| order-4700 | expired ×5 wait.5m | orders → order.created | {"orderId":4700,"customer":"ACME… | |
| order-4701 | expired ×5 wait.5m | orders → order.created | {"orderId":4701,"customer":"ACME… | |
| order-4702 | expired ×5 wait.5m | orders → order.created | {"orderId":4702,"customer":"ACME… | |
| order-4703 | rejected ×1 process | orders → order.created | {"orderId":4703,"customer":"Nort… |
| When | By | From → To | Replayed | Status |
|---|---|---|---|---|
| just now | nina | orders.dlq → original route | 3 | COMPLETED |
| Tue 09:14 | ops-bot | billing.invoice.dead → billing.invoice | 12 | COMPLETED |
| Mon 17:02 | tomas | orders.dlq → orders / order.retry | 0 | FAILED |
Every replay: who, when, from where to where, per-message outcome, payload preview.
orders.dlq · 9 messages ready
Nine orders from ACME failed with “No message” after five retries, and the consumer has been gone since 07:10. Here they are.
rabbitmqadmin get … | jq … | rabbitmqadmin publish …
Select, replay to the original route, done. Death headers stripped so they get a fresh retry budget.
Who replayed what last night?
The audit log answers it. User, time, source, target, every message, every outcome.
You keep the management UI, and you should. Warren does not compete with it and does not touch its job. It answers the four questions the console cannot, in the order on-call asks them.
Stays as it is. Warren needs no configure permission and never declares, deletes or purges.
Reads through the management API, publishes over AMQP with a least-privilege user.
Dead-letter counts on top, history charts, rules like “DLQ above 100 for 5 min” to Slack, Teams or a webhook.
Open the queue, read the messages, search for the order id the customer just gave you.
The x-death timeline: which queue rejected it, how often, when, and the retry counter that would bounce it again.
Replay to the original route, fix the payload first if needed, and let the audit log answer the postmortem.
Warren reads the same broker your team already runs and adds the parts the management console leaves out: meaning, history, and a safe way to act.
Peek at messages without consuming them. Pretty JSON, headers, and the complete x-death history: which queue rejected it, how often, when.
Selected messages or the first N, back to the exchange and routing key they came from. Or to any queue or exchange you name.
Edit payload and headers of a message in place. Drop the consumer's retry counter so the message gets a real second chance, not an instant bounce.
Who replayed what, when, from where to where, and what happened to each message. Fingerprints link every entry back to the queue.
Every queue sampled around the clock. Charts from 15 minutes to 7 days. Rules like “DLQ above 100 for 5 min” notify Slack, Teams or any webhook.
Any number of clusters and vhosts in one place. Viewers read, operators replay, admins manage. Sign in with your OIDC provider.
Each preview is the real flow, drawn from the actual screens. No mock data that hides the messy parts.
A message that was rejected five times carries that history in its headers. Replay it untouched and the consumer gives up instantly. Warren shows the headers, strips the bookkeeping on one click, and lets you correct the payload while you are there.
x-retry-count 5 → removed · x-last-exception-* removed · payload edited · audit says soWarren samples every queue around the clock. A rule like “orders.dlq above 100 for five minutes” fires once, reaches Slack, Teams or any webhook with a link to the queue, and sends the all-clear when it recovers. No flood, no silence.
MESSAGES_ABOVE · NO_CONSUMERS · GROWTH · re-notify after 4 h| Rule | Condition | Channel | State |
|---|---|---|---|
| Orders DLQ growing | messages > 100 for 300 s | #ops-alerts | OK |
| Billing stuck | no consumers, ready > 0 | #billing | OK |
Switch between production, billing and staging in the top bar. Viewers browse and learn, operators replay, admins manage rules and users. Roles come from your identity provider or from local accounts.
VIEWER · OPERATOR · ADMIN · OIDC roles claim → Warren role| Queue | Messages | Consumers |
|---|---|---|
| orders.dlqDLQ | 9 | 0 |
| billing.invoice.deadDLQ | 5 | 0 |
| orders.process | 3 | 4 |
| shipping.label | 7 | 2 |
One field searches payload, message id, correlation id and routing key at once, and narrows the table as you type. Every entry in the audit log carries a fingerprint you can paste here to find that exact message again, weeks later.
payload · message id · correlation id · routing key · fingerprint prefix| Message id | Routing key | Payload |
|---|---|---|
| order-4708 | order.created | {"orderId":4708,"customer":"ACME-0"… |
| order-4711 | order.created | {"orderId":4711,"customer":"Northwind"… |
| order-4713 | order.updated | {"orderId":4713,"customer":"ACME-2"… |
| order-4720 | order.created | {"orderId":4720,"customer":"Contoso"… |
| order-4734 | order.cancelled | {"orderId":4734,"customer":"ACME-1"… |
| order-4741 | order.created | {"orderId":4741,"customer":"Northwind"… |
Three roles that build on each other: viewers browse queues, payloads and history, operators additionally replay and edit, admins additionally manage alerts and users. Create accounts in the UI, lock one in a second, or map roles from your identity provider's claims. Warren refuses to remove its last administrator.
local accounts · OIDC claim → role · bcrypt · last-admin guard| On | Username | Roles | Last login |
|---|---|---|---|
| nina | ADMIN | 2 min ago | |
| tomas | OPERATOR | yesterday | |
| support | VIEWER | 3 days ago |
| VIEWER | OPERATOR | ADMIN | |
|---|---|---|---|
| Browse queues, payloads, history, audit | ● | ● | ● |
| Replay, edit payload and headers | – | ● | ● |
| Alert rules and channels | – | – | ● |
| Users and roles | – | – | ● |
A dead letter usually has a story. RabbitMQ writes it into headers nobody reads. Warren turns them into a timeline and tells you what a replay will actually do.
Replaying this message as-is would send it straight back to the DLQ: the consumer reads x-retry-count: 5 and gives up. Warren shows you that header, lets you reset it in the replay dialog, and records that you did.
Warren opens its own AMQP channel with publisher confirms. Nothing is shared with your consumers.
Messages are taken with basic.get and left unacknowledged, so each fetch returns the next one. Unselected messages are released back in place.
A selected message is published with mandatory=true. Only after the broker confirms does Warren acknowledge the source. Unroutable publishes come back as failures.
What was not moved is requeued in its original position. Closing the channel releases the rest, even if Warren crashes mid-way.
One container plus Postgres. Configure clusters with environment variables or a YAML file, put it behind your ingress, sign in with SSO. Air-gapped installations work: the licence is checked offline.
# the broker you already run, plus Warren and its database services: warren: image: ghcr.io/warren-dev/warren:1.0 ports: ["8080:8080"] environment: WARREN_RABBIT_MANAGEMENT_URL: http://rabbitmq:15672 WARREN_RABBIT_HOST: rabbitmq WARREN_RABBIT_USERNAME: warren WARREN_RABBIT_PASSWORD: ${RABBIT_PASSWORD} WARREN_DB_URL: jdbc:postgresql://postgres:5432/warren WARREN_OIDC_ISSUER_URI: https://login.example.com/realms/platform postgres: image: postgres:17-alpine $ docker compose up -d # then open http://localhost:8080
The Community edition is open source and stays free. Pro is in development. Both are waitlist-only until release; early-access pricing shown.
You get the image link the day it ships. Nothing else.
Early-access customers lock in this price. We reply personally.
No, and it is not trying to be. The management UI configures the broker and will keep doing that. Warren is for the people who run the traffic on it: reading messages, finding out why they died, alerting, replaying, and proving afterwards what was done. Most teams have both open.
No, by design. A tool that can replay production messages should not also be able to reshape the broker. Keeping Warren read-plus-publish is what lets you give it to a wider circle than the admin UI.
No. It reads queues, bindings and messages through the management API and publishes over AMQP. It never declares, deletes or purges anything.
The management tag plus read and write on the vhosts you configure. Configure permission is not required.
RabbitMQ marks peeked messages as redelivered. That is a property of the broker and is visible to consumers as a flag; the messages stay in place and in order.
3.13 and 4.x, classic and quorum queues. Streams are read-only for now.
In your network. Warren stores replay audit entries, queue metrics and users in your Postgres. Payload previews in the audit log are capped at 2 KB and can be turned off.
One e-mail on release day with the image and the compose file. Then it is three commands to your first replay.
Community first, Pro follows. No newsletter, no sharing of your address.