TL;DR:
- Bruno stores API collections as plain
.brufiles on disk — meaning they live in your repository alongside your code, get reviewed in PRs, and work with your existing git workflow - No cloud account required, no forced sync, no subscription to access features your team already uses
- The CLI (
@usebruno/cli) runs collections in CI/CD pipelines with the same files you use in the GUI - Not a full Postman replacement yet — no mock servers, no monitor scheduling, and the GUI is less polished — but for teams whose main pain is “why do my API collections not live in git,” Bruno solves that cleanly
Postman’s pivot to a cloud-first, subscription model left a lot of developers quietly annoyed. Collections that used to live locally now live in Postman’s cloud. Collaboration that used to be a simple file share now requires account management. The free tier’s limitations keep shifting. And the core frustration: the thing developers actually want — API collections in version control, alongside the code — isn’t really how Postman is designed to work.
Bruno takes the opposite approach. Collections are files. Files live in your repo. That’s the whole model, and it’s why developers who’ve spent time exporting Postman JSON and committing it to git have found Bruno immediately intuitive.
How Bruno Collections Work
A Bruno collection is a folder. Inside that folder, each API request is a .bru file — a simple text format that’s designed to be readable and diffable:
meta {
name: Get user by ID
type: http
seq: 1
}
get {
url: {{baseUrl}}/users/{{userId}}
body: none
auth: bearer
}
headers {
Accept: application/json
}
auth:bearer {
token: {{authToken}}
}
assert {
res.status: eq 200
res.body.id: eq {{userId}}
}
This is what a request file looks like. It’s readable in a code review, it shows meaningful diffs when you change an assertion or a header, and it’s just a file — git diff, git log, git blame all work exactly as you’d expect.
Environment variables ({{baseUrl}}, {{authToken}}) are stored separately. The pattern distinguishes between variables you commit (.env files checked into the repo, for non-sensitive shared config) and secrets (local only, excluded via .gitignore). This is a saner default than most API clients, which typically treat all environment values as equivalent.
Installation and Migration
Bruno is a desktop app (Mac, Windows, Linux AppImage/Snap) plus a CLI for automation. Install the app from usebruno.com or via a package manager:
# macOS
brew install bruno
# Debian/Ubuntu
snap install bruno
If you’re migrating from Postman, Bruno can import Postman Collection v2.1 JSON files:
- Export your Postman collection as JSON (Collection > Export > v2.1)
- File > Import in Bruno > Postman Collection
- Choose a folder in your repository to store the imported collection
The import handles requests, folders, variables, and pre-request scripts. Test assertions and environment variables need manual review — the Postman and Bruno variable/assertion syntaxes differ, and complex pre-request scripts may need rewriting.
Running Collections in CI
The @usebruno/cli package runs Bruno collections from the command line, making it straightforward to integrate into a CI/CD pipeline:
npm install -g @usebruno/cli
# Run a collection against the staging environment
bru run --env staging --reporter-junit results.xml
The reporter outputs JUnit XML, which most CI systems (GitHub Actions, GitLab CI, Jenkins) can parse and display as test results. Because the collection is just files in your repo, there’s no “export step” before CI runs — the CI workflow simply runs against the checked-in .bru files.
A GitHub Actions step might look like:
- name: Run API tests
run: |
npm install -g @usebruno/cli
bru run --env ci --reporter-junit test-results/api-tests.xml
working-directory: ./api-collections
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: api-test-results
path: test-results/
Environment-specific variables (base URLs, tokens) are passed via environment variables or a separate .env.ci file that’s populated from your secrets store. Sensitive values never live in the repo.
What Bruno Doesn’t Do (Yet)
Worth being honest about the gaps before switching:
No mock server: Postman’s mock server feature (generate a mock API from your collection’s example responses) doesn’t have a Bruno equivalent yet. If your team relies on mocks for frontend development against an in-progress backend, this is a real gap.
No scheduled monitors: Postman’s monitor feature (run a collection on a schedule and alert on failures) isn’t there. For API monitoring, you’d integrate the Bruno CLI into a scheduled CI job or use a dedicated monitoring tool.
Scripting differences: Bruno uses a JavaScript scripting environment similar to Postman’s, but there are API differences. pm.variables, pm.test, pm.response equivalents exist but aren’t identical. Complex Postman scripts need porting, not just copying.
GUI polish: The Bruno interface is functional but less polished than Postman’s. Search within large collections, response visualisation, and some keyboard shortcuts are rougher around the edges.
For teams whose primary use case is documenting, testing, and sharing REST/GraphQL/WebSocket APIs — and who want that work to live in version control — Bruno’s model is genuinely better suited than Postman’s cloud-first approach. For teams that rely heavily on mocks and monitoring, the trade-off calculation is less clear.
When to Switch
The clearest signal that Bruno is worth trying: if your team has a process where someone exports the Postman collection as JSON before committing, or if you’ve ever lost API request context because it lived in someone’s Postman workspace rather than the codebase, Bruno’s model fixes that specific problem well.
The migration from Postman is lower-friction than it might seem — most collections import cleanly, and the .bru format is simple enough that minor import issues are easily fixed. Running bru run in CI takes about an hour to set up properly once you have the collections imported.
For teams happy with Insomnia before its cloud migration controversy, Bruno is roughly the same level of capability with a better version control story. For teams already at peace with Postman, the switching cost is real — but if the “collections in git” problem is one you keep bumping into, it’s a switch worth making.