If you run BullMQ in production, you've probably had this moment: a customer tells you their payment/email/export never went through, you go digging, and you find the failed job sitting in Redis — recorded perfectly, retried exactly as configured, and completely silent. Nothing crashed.
No process exited non-zero. Nobody got paged.
That's not a bug. It's how a good queue is supposed to behave. This post is about turning that silence back into an alert, with three concrete approaches and their trade-offs.
Why BullMQ failures are silent by default
To a queue, failed is a normal lifecycle state, right next to completed and waiting. When a job throws and exhausts its attempts, BullMQ moves it into the failed set and picks up the next job. Your worker is healthier for handling it gracefully — which is exactly why none of your usual signals (a dead process, an HTTP 500, a crash loop) ever fire.






