Modern, sleek queue dashboard for BullMQ and Bull. Run it standalone with one command or embed it in your app.
View official docs →
Features
- 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.
Quick start
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.
Standalone
The quickest way to spin up bullstudio is via CLI:
npx bullstudio -r redis://localhost:6379
The 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-me
Docker
Bullstudio is available as a Docker image:
docker run -d \ -p 4000:4000 \ -e REDIS_URL=redis://host.docker.internal:6379 \ emirce/bullstudio
You 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-alpine
Embedded
Use 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-adapter
import { 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 Adapters
| 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, });
Queue Adapters
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.
Embedded Options
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.
Runnable Examples
| 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 |
Tech stack
Bullstudio's core is built with
Development
For detailed development guidelines visit the development section in the docs.
License
MIT



























