{"id":51468501,"url":"https://github.com/pontusab/workbench","last_synced_at":"2026-07-06T14:01:29.793Z","repository":{"id":360053740,"uuid":"1216925406","full_name":"pontusab/workbench","owner":"pontusab","description":"Open-source BullMQ dashboard — modern alternative to bull-board. 13 framework adapters, FlowProducer DAGs, schedulers, macOS app, Docker \u0026 MCP.","archived":false,"fork":false,"pushed_at":"2026-06-01T12:20:23.000Z","size":2634,"stargazers_count":391,"open_issues_count":12,"forks_count":33,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T11:34:11.052Z","etag":null,"topics":["background-jobs","bull-board","bullmq","dashboard","job-queue","monitoring","nodejs","queue","redis","typescript","workbench"],"latest_commit_sha":null,"homepage":"https://getworkbench.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pontusab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-21T11:19:32.000Z","updated_at":"2026-06-13T04:40:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pontusab/workbench","commit_stats":null,"previous_names":["pontusab/workbench"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/pontusab/workbench","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pontusab%2Fworkbench","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pontusab%2Fworkbench/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pontusab%2Fworkbench/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pontusab%2Fworkbench/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pontusab","download_url":"https://codeload.github.com/pontusab/workbench/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pontusab%2Fworkbench/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35193679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-06T02:00:07.184Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["background-jobs","bull-board","bullmq","dashboard","job-queue","monitoring","nodejs","queue","redis","typescript","workbench"],"created_at":"2026-07-06T14:01:27.944Z","updated_at":"2026-07-06T14:01:29.786Z","avatar_url":"https://github.com/pontusab.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://getworkbench.dev\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/pontusab/workbench/main/hero.png\" alt=\"Workbench — the missing dashboard for BullMQ\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# Workbench\n\nOpen-source BullMQ dashboard. Drop-in for any Node or Bun backend.\n\nWorkbench is a modern dashboard for [BullMQ](https://docs.bullmq.io/). Runs jobs, flows, schedulers and metrics, all served from your own backend behind your own auth.\n\n- Zero infrastructure — mounts as a route in your existing app, or run as a standalone Docker container\n- Adapters for Hono, Elysia, Express, Fastify, Koa, NestJS, AdonisJS, Next.js, TanStack Start, Astro, Nuxt, Bun.serve, and h3\n- Standalone image on GHCR (`ghcr.io/\u003cowner\u003e/workbench-standalone`) for Docker / Kubernetes deployments\n- MCP server for Cursor, Claude Desktop, Zed, and Continue.dev — drive your queues from your editor's chat\n- Flows \u0026 DAG view, metrics, schedulers, search\n- Dark-mode UI, basic-auth-protected by default\n- MIT licensed\n\nWebsite: [getworkbench.dev](https://getworkbench.dev) · [Documentation](https://getworkbench.dev/docs)\n\n## Migrating from bull-board?\n\nWorkbench is a drop-in alternative with thirteen first-party framework adapters, FlowProducer DAGs, error triage, and a keyboard-driven UI.\n\n| bull-board | Workbench |\n| --- | --- |\n| `@bull-board/express` | `@getworkbench/express` |\n| `@bull-board/fastify` | `@getworkbench/fastify` |\n| `@bull-board/koa` | `@getworkbench/koa` |\n| `@bull-board/nestjs` | `@getworkbench/nestjs` |\n| `@bull-board/hono` | `@getworkbench/hono` |\n| `@bull-board/h3` | `@getworkbench/h3` |\n| `@bull-board/elysia` | `@getworkbench/elysia` |\n\nRun `npx @getworkbench/cli init` to swap the mount in one command. Full comparison: [getworkbench.dev/blog/workbench-vs-bull-board](https://getworkbench.dev/blog/workbench-vs-bull-board)\n\n## Quick start\n\n```bash\nnpx @getworkbench/cli init\n```\n\nThe CLI detects your framework, installs the matching `@getworkbench/\u003cfw\u003e` package, injects the mount (or scaffolds a route file for Next.js), writes `.env.example` entries, and optionally drops a `docker-compose.yml` for Redis.\n\n## Manual setup\n\nPick the adapter that matches your stack:\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHono\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/hono bullmq hono\n```\n\n```ts\nimport { Hono } from \"hono\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/hono\";\n\nconst app = new Hono();\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\napp.route(\"/jobs\", workbench({ queues: [emailQueue] }));\n\nexport default app;\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eElysia\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nbun add @getworkbench/elysia bullmq elysia\n```\n\n```ts\nimport { Elysia } from \"elysia\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/elysia\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nnew Elysia()\n  .mount(\"/jobs\", workbench({ queues: [emailQueue], basePath: \"/jobs\" }))\n  .listen(3000);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eExpress\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/express bullmq express\n```\n\n```ts\nimport express from \"express\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/express\";\n\nconst app = express();\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\napp.use(\"/jobs\", workbench({ queues: [emailQueue] }));\napp.listen(3000);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eFastify\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/fastify bullmq fastify\n```\n\n```ts\nimport Fastify from \"fastify\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/fastify\";\n\nconst app = Fastify();\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nawait app.register(workbench({ queues: [emailQueue] }), { prefix: \"/jobs\" });\nawait app.listen({ port: 3000 });\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eNestJS\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/nestjs bullmq\n```\n\n```ts\nimport { NestFactory } from \"@nestjs/core\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/nestjs\";\nimport { AppModule } from \"./app.module\";\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  const emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\n  await workbench(app, \"/jobs\", { queues: [emailQueue] });\n\n  await app.listen(3000);\n}\nbootstrap();\n```\n\n\u003e Works on both the Express (default) and Fastify NestJS platforms.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAdonisJS\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/adonis bullmq @adonisjs/core\n```\n\n```ts\n// start/routes.ts\nimport router from \"@adonisjs/core/services/router\";\nimport { Queue } from \"bullmq\";\nimport { mountWorkbench } from \"@getworkbench/adonis\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nmountWorkbench(router, \"/jobs\", { queues: [emailQueue] });\n```\n\n\u003e Works with AdonisJS 6 and 7. See [examples/with-adonis](./examples/with-adonis/).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eNext.js (App Router)\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/next bullmq\n```\n\n```ts\n// app/jobs/[[...workbench]]/route.ts\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/next\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nexport const { GET, POST, PUT, PATCH, DELETE } = workbench({\n  queues: [emailQueue],\n  basePath: \"/jobs\",\n});\n```\n\n\u003e Next doesn't host BullMQ workers itself — run them in a sibling process. See [examples/with-next](./examples/with-next/).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTanStack Start\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/tanstack-start bullmq @tanstack/react-start\n```\n\n```ts\n// src/lib/workbench-handlers.ts\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/tanstack-start\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nexport const workbenchHandlers = workbench({\n  queues: [emailQueue],\n  basePath: \"/jobs\",\n});\n```\n\n```ts\n// src/routes/jobs.ts\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport { workbenchHandlers } from \"../lib/workbench-handlers\";\n\nexport const Route = createFileRoute(\"/jobs\")({\n  server: { handlers: workbenchHandlers },\n});\n```\n\n```ts\n// src/routes/jobs/$.ts\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport { workbenchHandlers } from \"../../lib/workbench-handlers\";\n\nexport const Route = createFileRoute(\"/jobs/$\")({\n  server: { handlers: workbenchHandlers },\n});\n```\n\n\u003e Register handlers on both `/jobs` and `/jobs/$` so the bare mount and nested paths work.\n\u003e TanStack Start doesn't host BullMQ workers itself — run them in a sibling process.\n\u003e See [examples/with-tanstack-start](./examples/with-tanstack-start/).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eKoa\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/koa bullmq koa\n```\n\n```ts\nimport Koa from \"koa\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/koa\";\n\nconst app = new Koa();\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\napp.use(workbench({ queues: [emailQueue], basePath: \"/jobs\" }));\napp.listen(3000);\n```\n\n\u003e Koa has no built-in mount helper — pass `basePath` so the middleware can\n\u003e match its own prefix and forward everything else to the next middleware.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAstro\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/astro bullmq\n```\n\n```ts\n// src/pages/jobs/[...workbench].ts\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/astro\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nexport const { GET, POST, PUT, PATCH, DELETE, prerender } = workbench({\n  queues: [emailQueue],\n  basePath: \"/jobs\",\n});\n```\n\n\u003e Astro must be in server output mode (`output: \"server\"` or `\"hybrid\"`).\n\u003e Astro doesn't host BullMQ workers itself — run them in a sibling process.\n\u003e See [examples/with-astro](./examples/with-astro/).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eNuxt\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/nuxt bullmq\n```\n\n```ts\n// server/routes/jobs/[...].ts\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/nuxt\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nexport default workbench({\n  queues: [emailQueue],\n  basePath: \"/jobs\",\n});\n```\n\n\u003e Nuxt's server runtime (Nitro / h3) doesn't host BullMQ workers itself —\n\u003e run them in a sibling process. See [examples/with-nuxt](./examples/with-nuxt/).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eBun.serve\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nbun add @getworkbench/bun bullmq\n```\n\n```ts\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/bun\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nconst handler = workbench({ queues: [emailQueue], basePath: \"/jobs\" });\n\nBun.serve({\n  port: 3000,\n  fetch(req) {\n    return handler(req, () =\u003e new Response(\"home\"));\n  },\n});\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eh3 (standalone — also for Nitro, SolidStart, Analog)\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\nnpm i @getworkbench/h3 bullmq h3\n```\n\n```ts\nimport { createServer } from \"node:http\";\nimport { createApp, createRouter, toNodeListener } from \"h3\";\nimport { Queue } from \"bullmq\";\nimport { workbench } from \"@getworkbench/h3\";\n\nconst emailQueue = new Queue(\"email\", { connection: { url: process.env.REDIS_URL! } });\n\nconst handler = workbench({ queues: [emailQueue], basePath: \"/jobs\" });\n\nconst router = createRouter().use(\"/jobs\", handler).use(\"/jobs/**\", handler);\nconst app = createApp().use(router);\n\ncreateServer(toNodeListener(app)).listen(3000);\n```\n\n\u003e h3's `**` only matches one-or-more sub-segments, so register the handler\n\u003e at both `/jobs` and `/jobs/**`.\n\n\u003c/details\u003e\n\nVisit `http://localhost:PORT/jobs`.\n\n## Configuration\n\n| Option     | Type                                | Description                                                |\n| ---------- | ----------------------------------- | ---------------------------------------------------------- |\n| `queues`   | `Queue[]`                           | BullMQ `Queue` instances to display. Required.             |\n| `auth`     | `{ username, password }`            | Basic auth credentials. Strongly recommended in prod.      |\n| `title`    | `string`                            | Dashboard title. Default: `\"Workbench\"`.                   |\n| `logo`     | `string`                            | Logo URL to display in the nav.                            |\n| `basePath` | `string`                            | Override base path detection. Required for `@getworkbench/elysia`, `@getworkbench/koa`, `@getworkbench/next`, `@getworkbench/tanstack-start`, `@getworkbench/astro`, `@getworkbench/nuxt`, and `@getworkbench/h3`. |\n| `readonly` | `boolean`                           | Disable actions (retry, remove, promote).                  |\n| `tags`     | `string[]`                          | Fields from `job.data` to extract as filterable tags.      |\n| `alerts`   | `AlertsOptions`                     | Self-hosted Slack/Discord/webhook alerting via BullMQ QueueEvents. |\n\n### Alerts\n\nAlerts are on by default. Configure Slack, Discord, or webhook **contact points** and **rules** in the dashboard **Alerts** page — notifications are only sent after you set that up. The model follows Grafana-style contact points + rules (unlike Bull Board, which has no built-in alerting). See [`@getworkbench/core` README](./packages/core/README.md) for setup steps.\n\n## Packages\n\n| Package                                                                 | Description                  |\n| ----------------------------------------------------------------------- | ---------------------------- |\n| [`@getworkbench/core`](./packages/core)                                 | Core + API router + UI       |\n| [`@getworkbench/hono`](./packages/hono)                                 | Hono adapter                 |\n| [`@getworkbench/elysia`](./packages/elysia)                             | Elysia adapter               |\n| [`@getworkbench/express`](./packages/express)                           | Express adapter              |\n| [`@getworkbench/fastify`](./packages/fastify)                           | Fastify adapter               |\n| [`@getworkbench/koa`](./packages/koa)                                   | Koa adapter                  |\n| [`@getworkbench/nestjs`](./packages/nestjs)                             | NestJS adapter               |\n| [`@getworkbench/adonis`](./packages/adonis)                             | AdonisJS adapter             |\n| [`@getworkbench/next`](./packages/next)                                 | Next.js App Router adapter   |\n| [`@getworkbench/tanstack-start`](./packages/tanstack-start)             | TanStack Start adapter       |\n| [`@getworkbench/astro`](./packages/astro)                               | Astro adapter                |\n| [`@getworkbench/nuxt`](./packages/nuxt)                                 | Nuxt (Nitro/h3) adapter      |\n| [`@getworkbench/h3`](./packages/h3)                                     | h3 adapter (Nitro/SolidStart/Analog) |\n| [`@getworkbench/bun`](./packages/bun)                                   | Bun.serve adapter            |\n| [`@getworkbench/cli`](./packages/cli)                                   | `npx @getworkbench/cli init` |\n| [`@getworkbench/mcp`](./packages/mcp)                                   | Model Context Protocol server — Cursor/Claude/Zed/Continue |\n| [`apps/standalone`](./apps/standalone)                                  | Standalone Bun server + Docker image (`ghcr.io/pontusab/workbench-standalone`) |\n\n[Hyper](https://hyperjs.ai) is distributed via a source-component registry, so its Workbench integration ships separately as a `hyper add @getworkbench` component in the [pontusab/hyper](https://github.com/pontusab/hyper) repo.\n\n## Docker (standalone)\n\nRun Workbench as its own container when you don't want to embed it in an app server. See [`apps/standalone`](./apps/standalone/README.md) for env vars and local dev.\n\n```bash\ndocker run --rm -p 3000:3000 \\\n  -e REDIS_URL=redis://host.docker.internal:6379 \\\n  -e QUEUE_NAMES=email,image \\\n  ghcr.io/pontusab/workbench-standalone:latest\n```\n\nTagged releases publish `ghcr.io/pontusab/workbench-standalone:\u003cversion\u003e` automatically.\n\n## FAQ\n\n**Is it BullMQ-only?** Yes. Bull (legacy) is not supported.\n\n**What Node version?** 18+ (or Bun 1.1+ for the Elysia and Bun.serve adapters).\n\n**What TypeScript version?** Any TypeScript 4.x or 5.x for the non-Hono adapters (Express, Fastify, NestJS, Next.js, Elysia) and `@getworkbench/core`. **`@getworkbench/hono` requires TypeScript 5.0+** because Hono 4's own bundled `.d.ts` uses `const` type parameters introduced in TS 5.0.\n\n**Can I run it without auth?** Yes, omit the `auth` option. Don't do that in production.\n\n**Does it require a separate service?** No for the embed path — it mounts as a route in your existing backend. Use the [standalone Docker image](./apps/standalone/README.md) when you want a separate container instead.\n\n## Development\n\n```bash\nbun i\nbun run build\nbun run typecheck\n\n# end-to-end smoke test against every example\ndocker compose up -d redis\nbun run smoke\n```\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## License\n\nMIT © [Pontus Abrahamsson](https://github.com/pontusab)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpontusab%2Fworkbench","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpontusab%2Fworkbench","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpontusab%2Fworkbench/lists"}