Modern, sleek queue dashboard for BullMQ and Bull. Run it standalone with one command or embed it in your app.
View official docs →
![]() |
![]() |
![]() |
- Queue overviews — live queue counts and job states surface stuck, failed, and backed-up work at a glance.
- Two ways to run — launch standalone with one command, or embed it inside your existing app.
- BullMQ & Bull — works with both BullMQ and legacy Bull queues via adapters.
- Framework adapters — support for Hono, Express, Fastify, Next.js, and NestJS.
- Flow view — visualize parent/child job trees and trace dependencies across your flows.
- Job control — retry, promote, remove, and clean jobs; inspect data, logs, and stack traces.
- Built-in auth — protect access with basic authentication or a read-only mode.
- Docker ready — run anywhere from the official image, standalone or in a compose stack.
Bullstudio runs in two modes:
- Standalone mode: run a separate dashboard process that connects directly to Redis and discovers queues on start up.
- Embedded mode: mount Bullstudio inside your app and expose only the queues you supply.
The quickest way to spin up bullstudio is via CLI:
npx bullstudio -r redis://localhost:6379The dashboard opens at http://localhost:4000 and connects to your local redis instance on port 6379.
bullstudio --help
bullstudio -r redis://:password@redis.example.com:6379 -p 8080 --no-open
bullstudio --prefix stage,stage2
bullstudio --username operator --password change-meBullstudio is available as a Docker image:
docker run -d \
-p 4000:4000 \
-e REDIS_URL=redis://host.docker.internal:6379 \
emirce/bullstudioYou can also run it in a Docker compose stack:
services:
bullstudio:
image: emirce/bullstudio
ports:
- "4000:4000"
environment:
REDIS_URL: redis://redis:6379
BULLSTUDIO_USERNAME: operator
BULLSTUDIO_PASSWORD: change-me
redis:
image: redis:7-alpineUse embedded mode if want to access Bullstudio from an existing application that is accessible via a public URL
Install one framework adapter and one queue adapter. Here as an example, we use Hono:
pnpm add @bullstudio/hono @bullstudio/bullmq-adapterimport { createBullMqQueueAdapter } from "@bullstudio/bullmq-adapter";
import { bullstudio } from "@bullstudio/hono";
import { Queue } from "bullmq";
import { Hono } from "hono";
import IORedis from "ioredis";
const connection = new IORedis(
process.env.REDIS_URL ?? "redis://localhost:6379",
{
maxRetriesPerRequest: null,
},
);
const emailQueue = new Queue("email", { connection });
const app = new Hono();
app.route(
"/ops/bullstudio",
bullstudio({
queues: [
createBullMqQueueAdapter(emailQueue, {
key: "email",
label: "Email",
}),
],
protection: {
type: "basic",
username: process.env.BULLSTUDIO_USERNAME ?? "operator",
password: process.env.BULLSTUDIO_PASSWORD ?? "change-me",
},
}),
);Open /ops/bullstudio. Dashboard assets and the private dashboard API are served under the same mount path.
| Framework | Package | Docs |
|---|---|---|
| Hono | @bullstudio/hono | Hono docs |
| Express | @bullstudio/express | Express docs |
| Fastify | @bullstudio/fastify | Fastify docs |
| Next.js App Router | @bullstudio/next | Next.js docs |
| NestJS | @bullstudio/nestjs | NestJS docs |
// Express
import { bullstudio } from "@bullstudio/express";
app.use("/ops/bullstudio", bullstudio({ queues }));// Fastify
import { bullstudio } from "@bullstudio/fastify";
await app.register(bullstudio({ queues }), {
prefix: "/ops/bullstudio",
});// app/ops/bullstudio/[[...bullstudio]]/route.ts
import { bullstudio } from "@bullstudio/next";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export const { GET, HEAD, POST } = bullstudio({
mountPath: "/ops/bullstudio",
queues,
});Bullstudio supports BullMQ and Bull (legacy) queues. You simply wrap your queue in the according adapter:
import { createBullMqQueueAdapter } from "@bullstudio/bullmq-adapter";
const queue = createBullMqQueueAdapter(emailQueue, {
key: "email",
label: "Email",
});import { createBullQueueAdapter } from "@bullstudio/bull-adapter";
const queue = createBullQueueAdapter(emailQueue, {
key: "email",
label: "Email",
});Embedded mode only shows supplied queues. Bullstudio does not discover all Redis queues in embedded mode and does not close host-owned queue connections.
bullstudio({
queues,
readOnly: true,
protection: {
type: "basic",
username: "operator",
password: process.env.BULLSTUDIO_PASSWORD ?? "change-me",
},
dashboardIdentity: {
title: "Production Queues",
},
documentIdentity: {
title: "Queue Ops",
favicon: "/favicon.ico",
},
});Set protection: { type: "disabled" } only when your host application protects the mount path.
| Framework | Command |
|---|---|
| Hono | pnpm --filter @bullstudio/example-hono-bullmq-embedded dev |
| Express | pnpm --filter @bullstudio/example-express-bullmq-embedded dev |
| Fastify | pnpm --filter @bullstudio/example-fastify-bullmq-embedded dev |
| Next.js App Router | pnpm --filter @bullstudio/example-next-bullmq-embedded dev |
| NestJS | pnpm --filter @bullstudio/example-nestjs-bullmq-embedded dev:express |
Bullstudio's core is built with
For detailed development guidelines visit the development section in the docs.
MIT


