Docker Integration
Bruno CLI is available as an official Docker image. Pull it, mount your collection, and run — no Node.js or npm needed on the host.
Where to pull from
usebruno namespace on Docker Hub is a Docker Verified Publisher. You will see the Verified Publisher badge on all usebruno/cli images, confirming they are official, trusted images published directly by the Bruno team.
| Registry | Image | Pull command |
|---|---|---|
| Docker Hub | usebruno/cli |
docker pull usebruno/cli |
| GitHub Container Registry | ghcr.io/usebruno/cli |
docker pull ghcr.io/usebruno/cli |
Both registries receive identical images on every release.
Image variants
| Variant | Base image | Size | When to use |
|---|---|---|---|
| Alpine (default) | node:22-alpine |
~141 MB | Best for most cases — smallest footprint. |
| Debian | node:22-slim |
~162 MB | When Alpine hits SSL or glibc compatibility issues. |
Both variants support linux/amd64 and linux/arm64.
Tags
| Style | Example | Behavior |
|---|---|---|
| Exact | usebruno/cli:3.4.2 |
Immutable — recommended for production CI. |
| Minor | usebruno/cli:3.4 |
Receives patch updates (3.4.x). |
| Major | usebruno/cli:3 |
Receives any 3.x.x update. |
| Latest | usebruno/cli:latest |
Moves only when explicitly tagged. |
Unsuffixed tags (:latest, :3.4.2, :3.4, :3) always resolve to the Alpine variant. Append -debian for the Debian variant (e.g. usebruno/cli:3.4.2-debian).
Quick start
Pull the image and verify it works:
docker pull usebruno/cli:latest
docker run usebruno/cli --version
docker pull downloads the image to your machine. docker run starts a container from that image — anything after the image name (usebruno/cli) is passed as arguments to bru, so --version prints the CLI version.
--rm to docker run automatically removes the container after it exits. It's not required, but keeps your system tidy by avoiding a buildup of stopped containers.
Run a collection
The Docker container needs access to your collection files to run them. Use the -v (volume mount) flag to map a directory on your host to /bruno inside the container. The path should always be mapped to /bruno — that's the working directory the CLI expects.
Since the image's entrypoint is already set to bru, running usebruno/cli run is the same as running bru run on your local machine. Any arguments you pass after the image name go straight to the CLI.
docker run --rm -v $(pwd):/bruno usebruno/cli run
docker run --rm -v ${PWD}:/bruno usebruno/cli run
docker run --rm -v %cd%:/bruno usebruno/cli run
Run a subfolder or single request
docker run --rm -v $(pwd):/bruno usebruno/cli run ./api-tests
docker run --rm -v $(pwd):/bruno usebruno/cli run ./api-tests/login.bru
Choose an environment
docker run --rm -v $(pwd):/bruno usebruno/cli run --env staging
Pass environment variables
docker run --rm -v $(pwd):/bruno usebruno/cli run --env local --env-var API_KEY=secret123
Generate a report
docker run --rm -v $(pwd):/bruno usebruno/cli run --reporter-junit results.xml
bru run options — environments, variables, reporters, tags, parallel execution, and more — see bru run.
Collection at a different path
If your collection is not in the current directory, point Docker at its path:
docker run --rm -v /path/to/your/collection:/bruno usebruno/cli run
Two usage patterns
Mount at runtime (no image build needed)
Best for local development and ad-hoc runs.
cd ~/my-api-tests
docker run --rm -v $(pwd):/bruno usebruno/cli run
Bake collection into a custom image
Best for CI pipelines and distributable test suites. Create a Dockerfile next to your bruno.json:
FROM usebruno/cli:3.4.2-alpine
COPY --chown=node:node . /bruno
CMD ["run", ".", "--env", "ci"]
Build and run:
docker build -t my-team/api-tests:v1 .
docker run --rm my-team/api-tests:v1
USER node (UID 1000) for security. Use --chown=node:node in COPY so the runtime user can write reports back to the mount.
CI/CD examples
GitHub Actions
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Bruno collection
run: |
docker run --rm \
-v ${{ github.workspace }}:/bruno \
usebruno/cli:latest run \
--reporter-junit results.xml
- name: Publish test report
uses: dorny/test-reporter@v3
if: success() || failure()
with:
name: Bruno Test Results
path: results.xml
reporter: java-junit
GitLab CI
api-tests:
image: usebruno/cli:latest
script:
- bru run --reporter-junit results.xml
artifacts:
reports:
junit: results.xml
Docker Compose
services:
bruno-cli:
image: usebruno/cli:latest
volumes:
- ./collection:/bruno
- ./reports:/reports
command:
run .
-r
--env ci
--reporter-json /reports/results.json
--reporter-junit /reports/results.xml
--reporter-html /reports/results.html
docker compose run bruno-cli
Image details
| Property | Value |
|---|---|
| Entrypoint | bru |
| Working directory | /bruno |
| Runtime user | node (UID 1000, non-root) |
| Architectures | linux/amd64, linux/arm64 |
Troubleshooting
docker run --rm \
-v $(pwd):/bruno \
--user "$(id -u):$(id -g)" \
usebruno/cli run --env ci
macOS Docker Desktop handles UID translation automatically.
docker run --rm -v $(pwd):/bruno usebruno/cli:3.4.2-debian run --env ci
-v flag uses a colon separator that collides with Windows drive letters. Use --mount instead:
docker run --rm \
--mount type=bind,source=C:\repo\collection,target=/bruno \
usebruno/cli run --env ci
Or run inside WSL where POSIX paths work directly.
run is non-recursive by default. If your collection has nested subfolders, add -r:
docker run --rm -v $(pwd):/bruno usebruno/cli run my-folder -r --env staging