Skip to main content
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

RegistryImagePull command
Docker Hubusebruno/clidocker pull usebruno/cli
GitHub Container Registryghcr.io/usebruno/clidocker pull ghcr.io/usebruno/cli
Both registries receive identical images on every release.

Image variants

VariantBase imageSizeWhen to use
Alpine (default)node:22-alpine~141 MBBest for most cases — smallest footprint.
Debiannode:22-slim~162 MBWhen Alpine hits SSL or glibc compatibility issues.
Both variants support linux/amd64 and linux/arm64.

Tags

StyleExampleBehavior
Exactusebruno/cli:3.4.2Immutable — recommended for production CI.
Minorusebruno/cli:3.4Receives patch updates (3.4.x).
Majorusebruno/cli:3Receives any 3.x.x update.
Latestusebruno/cli:latestMoves 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.
Adding --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

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
For all bru run options — environments, variables, reporters, tags, parallel execution, and more — see Run Command Options.

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
The base image runs as 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

PropertyValue
Entrypointbru
Working directory/bruno
Runtime usernode (UID 1000, non-root)
Architectureslinux/amd64, linux/arm64

Troubleshooting

Files written by the container may appear owned by a foreign UID. Fix with:
docker run --rm \
  -v $(pwd):/bruno \
  --user "$(id -u):$(id -g)" \
  usebruno/cli run --env ci
macOS Docker Desktop handles UID translation automatically.
The musl libc in Alpine can occasionally cause issues with native Node modules or SSL. Switch to the Debian variant:
docker run --rm -v $(pwd):/bruno usebruno/cli:3.4.2-debian run --env ci
The -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.
Bruno CLI’s 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

Resources