Run with Docker
ghcr.io/felixmosh/bull-board is the official image: the standalone CLI with a Node runtime wrapped around it. The dashboard runs as its own container next to your Redis, so there's nothing to install on the host and no app to mount an adapter into. That's usually what you want when the workers live in a repo you aren't editing, or when nothing in the stack is Node in the first place.
That serves the dashboard on http://127.0.0.1:3000 with every Bull and BullMQ queue it finds under the bull key prefix. host.docker.internal is how a container reaches a Redis running on the host: Docker Desktop provides it, and on plain Docker Engine you add --add-host host.docker.internal:host-gateway.
What's in the image
node:22-alpine and the published @bull-board/cli, nothing else. About 63 MB, built for linux/amd64 and linux/arm64, running as the unprivileged node user.
The entrypoint is the CLI itself, so anything after the image name is a flag exactly as the CLI guide documents it, and every BULL_BOARD_* variable behaves the same way. There's no image-specific configuration to learn. Two CLI defaults come preset, because they're the two that make no sense in a container:
Both are ordinary environment variables, so --host or your own -e BULL_BOARD_HOST still wins.
There's also a HEALTHCHECK polling the dashboard on its own port, so depends_on: { bull-board: { condition: service_healthy } } works for anything that should start behind it. Basic auth doesn't get in its way, since a 401 still proves the server is answering.
Tags
Pin the exact version if you'd rather nothing moved under you, or the major for patches without surprises. The package page lists every tag that exists.
Docker Compose
Next to a Redis of your own:
None of that is Compose specific. It's an ordinary container listening on 3000, so any orchestrator runs it the same way.
Configuring it
Flags after the image name, BULL_BOARD_* variables and a config file all work, and they resolve in that order. The CLI guide has the full table; these are the ones that come up in a container:
For anything longer, mount a config file. The working directory is /app, which is where the CLI looks, so a file mounted there is picked up without a --config flag as long as uid 1000 can read it:
Serving the dashboard under a path prefix, which is what a reverse proxy routing on the path needs, is --base-path:
Keeping it private
The container listens on every interface inside itself, so BULL_BOARD_USER and BULL_BOARD_PASSWORD (or --user and --password) aren't optional here, and the port mapping above publishes to 127.0.0.1 on the host rather than everywhere. The CLI warns at startup when it's bound to a non-loopback host with no auth set, since that's an unauthenticated dashboard with delete-job and obliterate-queue on it, reachable from anywhere that can route to the host. If all you need is visibility, --read-only turns off every destructive action.
Basic auth over plain HTTP still sends the credentials in the clear. Publishing the port beyond the host, whether that's a routable address, a cloud security group or a proxy without TLS, wants an SSH tunnel or TLS termination in front of it either way.
Building it yourself
The Dockerfile at the root of the repo is the one that produces the published image, and the CLI version is a build argument:
Worth doing if you need a different base image or an internal registry.
Without an image
The CLI also runs from npm inside a stock Node container. That re-resolves the package on every start, so it pins nothing and needs egress to the registry:
--host 0.0.0.0 and --no-open are spelled out here, since only the bull-board image presets them.