Open-source project
usebruno/bruno avatar
usebruno/bruno

Bruno: .bru files in Git, a CLI-only Docker image, and no cloud sync by design

Opensource IDE For Exploring and Testing API's (lightweight alternative to Postman/Insomnia)

48,393 stars3,106 forksJavaScriptMIT

At a glance

What is it?
Bruno is an MIT-licensed API client that stores collections as plain text .bru files in a folder you control, which is the whole difference from Postman. The same decision takes away shared workspaces, and the repository shows what fills the gap: seventeen workspaces, a separate CLI package, and Docker images published for the CLI alone.
Who is it for?
Adopt Bruno if your team already keeps API definitions in Git and wants requests reviewable in a diff, because the .bru folder makes that the default rather than an export you regenerate. Do not adopt it if you need a shared cloud workspace or account-based history, since the project rules out cloud sync permanently and every environment file has to exist on each machine separately.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • usebruno/bruno48,393 ★ · +2,615
Star history
Repository2026-07-212026-11-13
usebruno/bruno45.8k48.4k

2,615 Stars in the last 115 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Collections are .bru files in a folder, so Git is the only collaboration layer

The architecture is one decision carried through everything else. Bruno stores collections directly in a folder on your filesystem and writes them in a plain text markup language called Bru, so a request is a file with a name ending in .bru rather than a row in somebody's database. Version control is the collaboration story: the README says you can use Git, or any version control of your choice, over the collection.

What that buys is a reviewable diff. A changed header, a new body or an edited assertion shows up in a pull request as text, and a reviewer sees the request itself rather than a description of it.

What it costs is that nobody arbitrates a conflict for you. Two people editing the same .bru file produce a merge conflict in text with no server to resolve it and no visual diff tool behind a login, and the README points collaboration at your own repository rather than at a hosted workspace. That is a fine trade for a team already on Git and a poor one for a group of people who do not use it.

Offline-only is a commitment, and it removes the shared workspace

The offline promise is stated as a position rather than a setting: Bruno is offline-only, there are no plans to add cloud-sync ever, and the reasoning given is that data should stay on your device. The long-term vision discussion is linked as the place where that thinking is written out.

Take it at face value and plan around it. There is no account, no shared collection store and no server holding environment variables, so every developer's machine must carry its own environment files, and a variable added for a new test target is a commit that each person has to pull and configure. Nothing in the project will tell you that a teammate's local environment is missing a value.

The upside is the mirror image of that problem: a collection cannot leak to a service you did not choose, because there is no service to leak to. Teams under a data residency or bring-your-own-storage rule find that argument useful. Teams that relied on a hosted Postman workspace for onboarding new engineers lose the shared starting point and have to document it themselves.

Nine install routes, and the apt snippet ends at an unfinished redirect

Binaries are offered for Mac, Windows and Linux from the downloads page, and the same build is republished through package managers. The short ones:

sh
# On Mac via Homebrew
brew install bruno

# On Windows via Chocolatey
choco install bruno

# On Windows via Scoop
scoop bucket add extras
scoop install bruno
sh
# On Windows via winget
winget install Bruno.Bruno

# On Linux via Snap
snap install bruno

# On Linux via Flatpak
flatpak install com.usebruno.Bruno

# On Arch Linux via AUR
yay -S bruno

The Apt route is the one to look at twice, because the snippet in the README does not finish its own job:

sh
# On Linux via Apt
sudo mkdir -p /etc/apt/keyrings
sudo apt update && sudo apt install gpg curl
curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x9FA6017ECABE0266" \
  | gpg --dearmor \
  | sudo tee /etc/apt/keyrings/bruno.gpg >

That last line ends in a redirect with no destination and the block stops there, so copying it gives you a keyring directory and a half-finished pipe. Finish that path from the documentation rather than guessing the missing argument. Note also that the package names differ per manager, com.usebruno.Bruno for Flatpak and Bruno.Bruno for winget, so a provisioning script has to hardcode one of them. Snap and Flatpak also put the application inside a sandbox..

bru run is the whole documented CLI, and the reference lives on the docs site

The command line tool is a separate npm package from the desktop application, which is what makes CI possible without shipping an IDE into a pipeline. It installs globally:

sh
npm install -g @usebruno/cli

and then runs from the directory holding the collection, at three documented levels of scope:

sh
# Run every request in the collection
bru run

# Run a single request
bru run request.bru

# Run a folder against a specific environment
bru run folder --env Local

The environment name is passed with the --env flag on a folder run, which means the environments themselves are files in the collection rather than a hosted list, and that the name in your CI script has to match a file on disk.

Everything past these three forms is documented elsewhere. The README defers the full command reference to the Bruno CLI documentation, and it does not cover exit codes, reporting formats, retries or how a failed assertion fails the build. If your pipeline needs to parse a result or gate on one, that behaviour has to come from the docs site, not from the repository.

The published Docker images wrap the CLI, and the mount is the whole contract

Official images exist for the Bruno CLI and are published on every CLI release to two registries, with alpine and debian variants for linux/amd64 and linux/arm64. Their stated purpose is running collections in a pipeline or locally without installing Node.js or npm on the host.

sh
# Pull from Docker Hub
docker pull usebruno/cli:latest

# Or pull from the GitHub Container Registry
docker pull ghcr.io/usebruno/cli:latest

# Run a collection by mounting the current directory
docker run -v $(pwd):/bruno usebruno/cli run

The mount line is the entire contract, and it constrains your repository layout. The collection has to sit under the directory you mount, and the command is written in POSIX shell syntax, so `$(pwd)` has to be replaced on a Windows runner. The image is the CLI, not the application: there is no graphical Bruno in a container, so a job that needs to record or eyeball a response has no path here. The tag matrix, environment files and worked CI examples for GitHub Actions, GitLab CI and Jenkins are all in the documentation, and pinning a digest rather than latest is your call, since the images move on every CLI release.

Sixteen workspaces, an Electron shell, and two packages named like storage

The root package.json is private and declares seventeen workspaces under packages/. The names describe the split: bruno-electron is the desktop shell, bruno-app the interface, bruno-cli the command line tool, bruno-lang the Bru language, and bruno-schema and bruno-schema-types the format itself. Then there are bruno-requests, bruno-converters, bruno-toml, bruno-query, bruno-js, bruno-graphql-docs, bruno-common, bruno-tests, bruno-io, and two whose names point at persistence, bruno-sqlite and bruno-filestore.

An Electron shell explains the install weight and the update path: the desktop app arrives with a browser runtime inside it, which is where the size goes and why the package managers above matter more here than for a native binary.

The two storage packages are the part worth asking about. The names alone do not say what bruno-sqlite and bruno-filestore store.

Bruno against Postman: the difference is where the collection lives

The project description calls Bruno a lightweight alternative to Postman and Insomnia, and the README frames it as an attempt to change the status quo those tools represent. The meaningful difference is not the request editor, it is the address of the data. Postman keeps collections attached to an account and a workspace; Bruno keeps them in a folder you version.

That has follow-on effects worth pricing before you move. Merge conflicts become your problem instead of the vendor's. Environment variables become committed files instead of a settings page. Request history is whatever your repository records. And a collection written in Postman's format is not a Bruno collection, so migrating is work rather than an import button: packages/bruno-converters exists in the workspace list, which tells you a conversion path was built..

On features, the README is thin where it matters most. The Features section holds two headings with images and almost no text, and the commercial note says only that the majority of features are free and open source. What is verifiable from the repository is the file format, the CLI, the containers and the package manager list. Judge the rest against the pricing page and the documentation.

MIT code, a trademarked name, and three minor releases since July

The code is MIT, stated in the repository's license.md, and the default branch is main with the last push on 2026-11-02. The latest releases are v4.2.0 on 2026-09-23, v4.2.1 on 2026-09-30 and v4.3.0 on 2026-10-22, which for a tool that owns your API definitions means a scheduled upgrade habit rather than an occasional one.

Two things sit outside the MIT grant. The README states that Bruno is a trademark held by Anoop M D, so a fork that keeps the name is using someone else's mark, and the logo comes from OpenMoji under CC BY-SA 4.0, which is a share-alike licence on the asset rather than on the code. Neither stops you from contributing, but both need handling in a white-label build.

The repository also invests in its contributors. Twenty README translations live in docs/readme, and the root carries CODING_STANDARDS.md, governance.md, security.md, an eslint config, husky hooks, and three Playwright configurations including a benchmark one..

Editorial conclusion

Adopt Bruno if your team already keeps API definitions in Git and wants requests reviewable in a diff, because the .bru folder makes that the default rather than an export you regenerate. Do not adopt it if you need a shared cloud workspace or account-based history, since the project rules out cloud sync permanently and every environment file has to exist on each machine separately. Verify two things before you migrate a collection: which features sit behind the pricing page, since the README says only that the majority are free, and whether your existing collection survives the converters package, since packages/bruno-converters exists in the workspace list.

Frequently asked questions

How do I use Bruno for API testing?

Collections are folders on your filesystem written in a plain text markup language called Bru, and requests are run either from the desktop application or from the CLI with bru run, which executes every request in the collection. A single request or a folder can be run instead, and a folder run can be pointed at a named environment with bru run folder --env Local.

How do I install Bruno?

Binaries for Mac, Windows and Linux are offered on the downloads page, and the same build is republished through package managers including Homebrew, Chocolatey, Scoop, Snap, Flatpak, Apt and the AUR. The Apt snippet in the README ends at an unfinished shell redirect, so take that route from the documentation instead.

How do I install Bruno on a Mac?

The README gives a Homebrew command for the Mac, and also points to binary downloads on the project website for Mac, Windows and Linux. It does not give a curl or installer script for the desktop application.

How do I install Bruno on Ubuntu?

Three Linux routes are given: Snap with snap install bruno, Flatpak with flatpak install com.usebruno.Bruno, and an Apt sequence that installs gpg and curl and imports a key into /etc/apt/keyrings, though the last line of that snippet is a redirect with no destination. The AUR line, yay -S bruno, covers Arch-based distributions.

How do I use the Bruno CLI?

Install the separate npm package with npm install -g @usebruno/cli, then run bru from the directory holding your collection. The documented commands are bru run for the whole collection, bru run request.bru for one request, and bru run folder --env Local for a folder against a named environment.

Can Bruno run collections in Docker for CI?

Yes, for the CLI. Official images are published on every CLI release to Docker Hub and the GitHub Container Registry, with alpine and debian variants for linux/amd64 and linux/arm64, and you mount the collection directory with docker run -v $(pwd):/bruno usebruno/cli run so no Node.js or npm is needed on the host.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/usebruno-bruno.svg)](https://hysenlabs.com/projects/usebruno-bruno)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/usebruno-bruno.svg)](https://hysenlabs.com/projects/usebruno-bruno)

Was this useful?