Skip to content

Commit c4c79c0

Browse files
pontusabcursoragent
andcommitted
docs: add Docker to marketing site and README
Document the standalone GHCR image in the hero strip, install section, llms.txt, comparison FAQ, and root README. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent d70023e commit c4c79c0

7 files changed

Lines changed: 109 additions & 17 deletions

File tree

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ Open-source BullMQ dashboard. Drop-in for any Node or Bun backend.
1010

1111
Workbench 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.
1212

13-
- Zero infrastructure — mounts as a route in your existing app
13+
- Zero infrastructure — mounts as a route in your existing app, or run as a standalone Docker container
1414
- Adapters for Hono, Elysia, Express, Fastify, Koa, NestJS, AdonisJS, Next.js, TanStack Start, Astro, Nuxt, Bun.serve, and h3
15+
- Standalone image on GHCR (`ghcr.io/<owner>/workbench-standalone`) for Docker / Kubernetes deployments
1516
- MCP server for Cursor, Claude Desktop, Zed, and Continue.dev — drive your queues from your editor's chat
1617
- Flows & DAG view, metrics, schedulers, search
1718
- Dark-mode UI, basic-auth-protected by default
@@ -399,9 +400,23 @@ Visit `http://localhost:PORT/jobs`.
399400
| [`@getworkbench/bun`](./packages/bun) | Bun.serve adapter |
400401
| [`@getworkbench/cli`](./packages/cli) | `npx @getworkbench/cli init` |
401402
| [`@getworkbench/mcp`](./packages/mcp) | Model Context Protocol server — Cursor/Claude/Zed/Continue |
403+
| [`apps/standalone`](./apps/standalone) | Standalone Bun server + Docker image (`ghcr.io/pontusab/workbench-standalone`) |
402404

403405
[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.
404406

407+
## Docker (standalone)
408+
409+
Run 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.
410+
411+
```bash
412+
docker run --rm -p 3000:3000 \
413+
-e REDIS_URL=redis://host.docker.internal:6379 \
414+
-e QUEUE_NAMES=email,image \
415+
ghcr.io/pontusab/workbench-standalone:latest
416+
```
417+
418+
Tagged releases publish `ghcr.io/pontusab/workbench-standalone:<version>` automatically.
419+
405420
## FAQ
406421

407422
**Is it BullMQ-only?** Yes. Bull (legacy) is not supported.
@@ -412,7 +427,7 @@ Visit `http://localhost:PORT/jobs`.
412427

413428
**Can I run it without auth?** Yes, omit the `auth` option. Don't do that in production.
414429

415-
**Does it require a separate service?** No. It mounts as a route in your existing backend.
430+
**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.
416431

417432
## Development
418433

apps/web/public/llms.txt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
> Workbench is an open-source, MIT-licensed BullMQ dashboard. It ships as a
44
> native macOS desktop app for local-first queue inspection, and as a
55
> drop-in server-mounted dashboard with first-party adapters for thirteen
6-
> Node.js frameworks. Workbench is the modern alternative to bull-board:
6+
> Node.js frameworks, plus a standalone Docker image for container deployments.
7+
> Workbench is the modern alternative to bull-board:
78
> wider framework coverage, FlowProducer DAG visualisation, error triage
89
> grouped by exception class, scheduler timeline for cron / delayed jobs,
910
> and a keyboard-driven UI.
@@ -15,6 +16,7 @@
1516
- **Source**: https://github.com/pontusab/workbench
1617
- **Install (any supported framework)**: `npx @getworkbench/cli init`
1718
- **Desktop app**: macOS 11+, Apple-signed, auto-updates from signed releases
19+
- **Docker**: `ghcr.io/pontusab/workbench-standalone` — standalone Bun server; set `REDIS_URL` and `QUEUE_NAMES`
1820
- **Author**: Pontus Abrahamsson — https://x.com/pontusab
1921

2022
## Supported frameworks (first-party adapters)
@@ -36,6 +38,19 @@ example app in the monorepo.
3638
- Bun (Bun.serve) — https://www.npmjs.com/package/@getworkbench/bun
3739
- h3 — https://www.npmjs.com/package/@getworkbench/h3
3840

41+
## Docker (standalone)
42+
43+
Run Workbench without embedding it in your app server:
44+
45+
```bash
46+
docker run --rm -p 3000:3000 \
47+
-e REDIS_URL=redis://host.docker.internal:6379 \
48+
-e QUEUE_NAMES=default \
49+
ghcr.io/pontusab/workbench-standalone:latest
50+
```
51+
52+
Docs: https://github.com/pontusab/workbench/tree/main/apps/standalone
53+
3954
## AI agent integrations (MCP)
4055

4156
MCP server that lets Cursor, Claude Desktop, Zed, Continue.dev and any

apps/web/src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ const siteJsonLd = {
115115
url: SITE_URL,
116116
name: "Workbench",
117117
description:
118-
"The missing dashboard for BullMQ — a local-first native macOS app and embeddable Node.js dashboard with first-party adapters for thirteen frameworks.",
118+
"The missing dashboard for BullMQ — a local-first native macOS app, embeddable Node.js dashboard with thirteen framework adapters, and a standalone Docker image.",
119119
publisher: { "@id": `${SITE_URL}/#organization` },
120120
inLanguage: "en-US",
121121
},

apps/web/src/app/page.tsx

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
AdonisLogo,
2121
AstroLogo,
2222
BunLogo,
23+
DockerLogo,
2324
ElysiaLogo,
2425
ExpressLogo,
2526
FastifyLogo,
@@ -45,6 +46,12 @@ import { ThemeToggle } from "../components/theme-toggle";
4546
const MAC_DOWNLOAD_URL =
4647
"https://github.com/pontusab/workbench/releases/latest";
4748
const GITHUB_URL = "https://github.com/pontusab/workbench";
49+
const STANDALONE_DOCS_URL = `${GITHUB_URL}/tree/main/apps/standalone`;
50+
const STANDALONE_IMAGE = "ghcr.io/pontusab/workbench-standalone:latest";
51+
const DOCKER_RUN_COMMAND = `docker run --rm -p 3000:3000 \\
52+
-e REDIS_URL=redis://host.docker.internal:6379 \\
53+
-e QUEUE_NAMES=default \\
54+
${STANDALONE_IMAGE}`;
4855
const SPONSORS_URL = "https://github.com/sponsors/pontusab";
4956
const TWITTER_URL = "https://x.com/pontusab";
5057
const DOCS_URL = "https://github.com/pontusab/workbench#readme";
@@ -89,6 +96,17 @@ const frameworks = [
8996
{ name: "h3", Logo: H3Logo, ...npmAdapter("h3") },
9097
];
9198

99+
/** Deployment option — not an npm adapter; links to standalone docs. */
100+
const dockerDeployment = {
101+
name: "Docker",
102+
Logo: DockerLogo,
103+
href: STANDALONE_DOCS_URL,
104+
title: "Run Workbench as a standalone Docker container",
105+
};
106+
107+
/** Hero marquee shows framework adapters plus Docker. */
108+
const heroStrip = [...frameworks, dockerDeployment];
109+
92110
/**
93111
* Homepage JSON-LD.
94112
*
@@ -123,7 +141,7 @@ const homepageJsonLd = {
123141
url: SITE_URL,
124142
applicationCategory: "DeveloperApplication",
125143
applicationSubCategory: "Job queue dashboard",
126-
operatingSystem: "macOS, Node.js, Bun",
144+
operatingSystem: "macOS, Node.js, Bun, Docker",
127145
softwareVersion: "latest",
128146
license: "https://opensource.org/licenses/MIT",
129147
downloadUrl: MAC_DOWNLOAD_URL,
@@ -137,7 +155,7 @@ const homepageJsonLd = {
137155
"Scheduler timeline for cron and delayed jobs",
138156
"Error triage grouped by exception class with 24h trend lines",
139157
"Open failed-job stack traces in Cursor or VS Code with one click",
140-
"Native macOS desktop app and embeddable server dashboard",
158+
"Native macOS desktop app, embeddable server dashboard, and standalone Docker image",
141159
"MCP server for Cursor, Claude Desktop, Zed, and Continue.dev",
142160
],
143161
offers: {
@@ -188,6 +206,22 @@ const homepageJsonLd = {
188206
},
189207
],
190208
},
209+
{
210+
"@type": "ItemList",
211+
"@id": `${SITE_URL}/#container-deployments`,
212+
name: "Container deployments for Workbench",
213+
description:
214+
"Standalone Docker image for running Workbench without embedding it in an app server.",
215+
numberOfItems: 1,
216+
itemListElement: [
217+
{
218+
"@type": "ListItem",
219+
position: 1,
220+
name: "workbench-standalone on GHCR",
221+
url: STANDALONE_DOCS_URL,
222+
},
223+
],
224+
},
191225
],
192226
};
193227

@@ -321,7 +355,8 @@ function Hero() {
321355

322356
<p className="mt-6 max-w-2xl text-balance text-base leading-relaxed text-[color:var(--color-muted-foreground)] md:text-lg">
323357
Inspect, debug, and replay your BullMQ queues. Run it as a native
324-
desktop app, or drop the same dashboard into any Node framework.
358+
desktop app, drop it into any Node framework, or deploy it as a
359+
standalone Docker container.
325360
</p>
326361

327362
<div className="mt-9 flex flex-col items-center gap-3 sm:flex-row sm:gap-4">
@@ -387,10 +422,10 @@ function HeroFrameworks() {
387422
return (
388423
<div
389424
className="hero-marquee mb-14 text-[color:var(--color-foreground)]"
390-
aria-label="Works with these frameworks"
425+
aria-label="Works with these frameworks and Docker"
391426
>
392427
<div className="hero-marquee-track">
393-
{frameworks.map(({ name, Logo, href, title }) => (
428+
{heroStrip.map(({ name, Logo, href, title }) => (
394429
<a
395430
key={`primary-${name}`}
396431
href={href}
@@ -406,7 +441,7 @@ function HeroFrameworks() {
406441
{/* Visual duplicate so the loop is seamless. These are presentational
407442
only — screen readers announce the framework list once via the
408443
primary copy above, then ignore everything inside this group. */}
409-
{frameworks.map(({ name, Logo }) => (
444+
{heroStrip.map(({ name, Logo }) => (
410445
<span
411446
key={`mirror-${name}`}
412447
className="hero-marquee-item inline-flex items-center gap-1.5 opacity-70"
@@ -604,16 +639,16 @@ function InstallSection() {
604639
Install
605640
</div>
606641
<h2 className="mt-3 text-balance text-3xl font-semibold tracking-tight md:text-4xl">
607-
One dashboard. Two ways to run it.
642+
One dashboard. Three ways to run it.
608643
</h2>
609644
<p className="mt-3 text-[color:var(--color-muted-foreground)]">
610-
Native macOS app for local debugging, or one command to mount the
611-
same dashboard inside your Node server. Same UI, same open-source
612-
core.
645+
Native macOS app for local debugging, one command to mount the
646+
dashboard inside your Node server, or a standalone Docker container
647+
pointed at your Redis. Same UI, same open-source core.
613648
</p>
614649
</div>
615650

616-
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
651+
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
617652
<InstallCard
618653
eyebrow="Desktop"
619654
title="Native macOS app"
@@ -637,6 +672,15 @@ function InstallSection() {
637672
body="One command wires the dashboard into your existing server. Works with Hono, Elysia, Express, Fastify, NestJS, AdonisJS, Next.js, TanStack Start, Koa, Astro, Nuxt, Bun, and h3 — share the same Redis as your workers."
638673
action={<CopyCommand command={INSTALL_COMMAND} variant="button" />}
639674
/>
675+
676+
<InstallCard
677+
eyebrow="Docker"
678+
title="Standalone container"
679+
body="Run Workbench without embedding it in your app. Set REDIS_URL and QUEUE_NAMES — published to GHCR on every release tag."
680+
action={
681+
<CopyCommand command={DOCKER_RUN_COMMAND} variant="button" />
682+
}
683+
/>
640684
</div>
641685
</div>
642686
</section>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { SVGProps } from "react";
2+
3+
export function DockerLogo(props: SVGProps<SVGSVGElement>) {
4+
return (
5+
<svg
6+
role="img"
7+
viewBox="0 0 24 24"
8+
fill="currentColor"
9+
xmlns="http://www.w3.org/2000/svg"
10+
aria-hidden="true"
11+
{...props}
12+
>
13+
<title>Docker</title>
14+
<path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185zm-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185zm0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186zm-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186zm-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.186.186 0 00-.186-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186zm5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185zm-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.184.185zm-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.185-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.084.185.185.185zm-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.184.185zM23.763 9.89c-.065.051-.672.483-1.342.984-3.672 2.677-7.244 4.313-10.592 4.863-3.35.55-6.428.281-9.165-.79C3.53 13.66 2.504 12.3 1.77 10.554.035 6.656 1.36 2.65 4.88.483 8.4-1.684 13.59-.96 17.65 2.318c.768.584 1.463 1.304 2.033 2.13.174.233.371.505.571.787.744 1.108 1.253 2.003 1.509 2.655zm-8.008-2.376c0 1.87-1.515 3.385-3.385 3.385S8.985 9.384 8.985 7.514s1.515-3.385 3.385-3.385 3.385 1.515 3.385 3.385z" />
15+
</svg>
16+
);
17+
}

apps/web/src/components/logos/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export { AdonisLogo } from "./adonis";
22
export { AstroLogo } from "./astro";
33
export { BunLogo } from "./bun";
4+
export { DockerLogo } from "./docker";
45
export { ElysiaLogo } from "./elysia";
56
export { ExpressLogo } from "./express";
67
export { FastifyLogo } from "./fastify";

apps/web/src/lib/blog/comparison.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export const COMPARISON_FAQ: Array<{ question: string; answer: string }> = [
119119
{
120120
question: "What is the best alternative to bull-board?",
121121
answer:
122-
"Workbench is the modern open-source alternative to bull-board for BullMQ. It ships first-party adapters for thirteen Node.js frameworks (Hono, Elysia, Express, Fastify, NestJS, AdonisJS, Next.js, TanStack Start, Koa, Astro, Nuxt, Bun, and h3), adds a native macOS desktop app for local-first inspection, and includes FlowProducer DAG visualisation, error triage grouped by exception class, and a scheduler timeline — features bull-board does not ship out of the box.",
122+
"Workbench is the modern open-source alternative to bull-board for BullMQ. It ships first-party adapters for thirteen Node.js frameworks (Hono, Elysia, Express, Fastify, NestJS, AdonisJS, Next.js, TanStack Start, Koa, Astro, Nuxt, Bun, and h3), a native macOS desktop app for local-first inspection, and a standalone Docker image (`ghcr.io/pontusab/workbench-standalone`) for container deployments. It also includes FlowProducer DAG visualisation, error triage grouped by exception class, and a scheduler timeline — features bull-board does not ship out of the box.",
123123
},
124124
{
125125
question: "Does Workbench work with the same BullMQ version as bull-board?",
@@ -160,7 +160,7 @@ export const COMPARISON_FAQ: Array<{ question: string; answer: string }> = [
160160
export const BULL_BOARD_HEADLINES = [
161161
{
162162
title: "A native desktop app, not just an embeddable UI",
163-
body: "Bull Board is a server-side dashboard you mount into your app. Workbench is both — embed it into your server (same one-liner) or run the native macOS app pointed at your Redis URL with zero server changes.",
163+
body: "Bull Board is a server-side dashboard you mount into your app. Workbench is both — embed it into your server (same one-liner), run the native macOS app pointed at your Redis URL with zero server changes, or deploy the standalone Docker image when you want a separate container.",
164164
},
165165
{
166166
title: "Wider framework coverage, with first-party adapters",

0 commit comments

Comments
 (0)