Uptime Monitoring for Bruno API Client Deployments 2026
Bruno is the open-source, offline-first API client that stores collections as plain files in your git repo. No account required, no cloud sync, no vendor lock-in. Teams adopt Bruno specifically because it treats API collections like code.
But Bruno's job is to let you interact with APIs — not to watch them. This guide shows you how to add continuous uptime monitoring to every service in your Bruno collections.
Why Bruno collections aren't enough for production monitoring
Bruno collections live in your repo and run when developers trigger them. They're perfect for development, debugging, and PR validation. They're not designed for continuous monitoring.
The gap:
Bruno request → success (service is up at 10:03 AM)
...
Service goes down at 10:47 AM
...
Next Bruno request → failure (developer hits this at 2:15 PM)
3.5 hours of undetected downtime
External uptime monitoring closes that gap. Vigilmon probes your endpoints every 5 minutes from multiple regions — continuously, automatically, whether any developer has Bruno open or not.
Step 1: Add a health endpoint to each service
For each service in your Bruno collection, add a /health endpoint that reflects real dependency status.
Rust / Axum:
// src/routes/health.rs
use axum::{http::StatusCode, Json};
use serde::Serialize;
use sqlx::PgPool;
use std::collections::HashMap;
#[derive(Serialize)]
pub struct HealthResponse {
status: String,
checks: HashMap<String, CheckResult>,
}
#[derive(Serialize)]
pub struct CheckResult {
status: String,
#[serde(skip_serializing_if = "Option::is_none")]
detail: Option<String>,
}
pub async fn health_check(
axum::extract::State(pool): axum::extract::State<PgPool>,
) -> (StatusCode, Json<HealthResponse>) {
let mut checks = HashMap::new();
match sqlx::query("SELECT 1").fetch_one(&pool).await {
Ok(_) => {
checks.insert("database".to_string(), CheckResult {
status: "ok".to_string(),
detail: None,
});
}
Err(e) => {
checks.insert("database".to_string(), CheckResult {
status: "error".to_string(),
detail: Some(e.to_string()),
});
}
}
let all_ok = checks.values().all(|c| c.status == "ok");
let status_code = if all_ok { StatusCode::OK } else { StatusCode::SERVICE_UNAVAILABLE };
(status_code, Json(HealthResponse {
status: if all_ok { "ok".to_string() } else { "degraded".to_string() },
checks,
}))
}
Elixir / Phoenix:
# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
def check(conn, _params) do
checks = %{
database: check_database()
}
all_ok = Enum.all?(checks, fn {_, v} -> v.status == "ok" end)
status = if all_ok, do: 200, else: 503
conn
|> put_status(status)
|> json(%{
status: if(all_ok, do: "ok", else: "degraded"),
checks: checks
})
end
defp check_database do
case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1", []) do
{:ok, _} -> %{status: "ok"}
{:error, e} -> %{status: "error", detail: Exception.message(e)}
end
end
end
Add the health request to your Bruno collection — create a GET /health request in a _monitoring folder at the root of your collection. This way the endpoint is documented alongside all other requests.
my-api/
├── .bruno/
│ └── bruno.json
├── _monitoring/
│ └── health-check.bru
├── auth/
│ ├── login.bru
│ └── logout.bru
└── users/
├── get-users.bru
└── create-user.bru
# _monitoring/health-check.bru
meta {
name: Health Check
type: http
seq: 1
}
get {
url: {{baseUrl}}/health
body: none
auth: none
}
tests {
test("Service is healthy", function() {
expect(res.status).to.equal(200);
expect(res.body.status).to.equal("ok");
});
}
Step 2: Set up external HTTP monitoring with Vigilmon
With health endpoints in place, point Vigilmon at them:
- Sign up at vigilmon.online — free tier, no credit card
- Click New Monitor → HTTP
- Enter
https://yourservice.com/health - Set the check interval (5 minutes on free tier)
- Save
Create monitors that mirror your Bruno environments. Bruno stores environments as files in environments/:
my-api/
└── environments/
├── production.bru
├── staging.bru
└── local.bru
For each named environment, create a Vigilmon monitor:
| Bruno Environment | Monitor URL |
|---|---|
| production.bru | https://api.yourdomain.com/health |
| staging.bru | https://staging.api.yourdomain.com/health |
Step 3: Heartbeat monitoring for Bruno / Bru CLI pipelines
Bruno's CLI (@usebruno/cli) lets you run collections in CI. Add heartbeat monitoring so you know if the pipeline stops running.
In Vigilmon:
- New Monitor → Heartbeat
- Set expected interval (e.g. 25 hours for daily CI)
- Copy the ping URL
GitHub Actions with Bruno CLI:
# .github/workflows/bruno-tests.yml
name: Bruno API Tests
on:
schedule:
- cron: '0 6 * * *' # Daily at 6 AM UTC
push:
branches: [main]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Bruno CLI
run: npm install -g @usebruno/cli
- name: Run Bruno collection
run: |
bru run \
--env production \
--output results.json \
collections/my-api
- name: Ping heartbeat on success
if: success()
run: curl -fsS "${{ secrets.BRUNO_HEARTBEAT_URL }}"
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: bruno-test-results
path: results.json
Add BRUNO_HEARTBEAT_URL to your GitHub repository secrets.
Pre-commit hook for local validation:
Since Bruno stores collections in git, you can run a subset of tests as a pre-commit hook:
#!/bin/sh
# .git/hooks/pre-commit
echo "Running Bruno smoke tests..."
bru run --env local collections/my-api/_monitoring
if [ $? -ne 0 ]; then
echo "Health checks failed — is your local service running?"
exit 1
fi
This is local validation only — the CI heartbeat is what tells you about production.
Step 4: Monitor authenticated endpoints
Bruno handles authentication through its environment files. For monitoring authenticated endpoints, Vigilmon supports custom headers:
- In Vigilmon: New Monitor → HTTP → Advanced
- Add headers:
| Header | Value |
|---|---|
| Authorization | Bearer your-monitoring-token |
| X-Monitor-Key | your-secret-key |
Create a dedicated monitoring API token with read-only permissions — don't use production user credentials in your monitor config.
// Express middleware example
router.get('/health/authenticated', authenticateMonitor, async (req, res) => {
// Deep health check available only to monitoring systems
const checks = await deepHealthCheck();
res.json(checks);
});
function authenticateMonitor(req, res, next) {
const key = req.headers['x-monitor-key'];
if (key !== process.env.MONITOR_API_KEY) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
}
Step 5: Alert configuration
In Vigilmon Notifications → New Channel, set up alerts that go to wherever your team already communicates:
Slack:
POST to: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
Alert message example:
🔴 DOWN: api.yourdomain.com/health
Status: 503 Service Unavailable
Started: 3 min ago | Regions: US-East, EU-West
Discord:
- Server Settings → Integrations → Webhooks
- Create webhook for
#alertschannel - Paste URL in Vigilmon
Email escalation:
Use Vigilmon's multi-step alerting: first alert goes to Slack, if not acknowledged in 15 minutes it emails the on-call engineer.
Step 6: Add a status page
A public status page tells users what's happening without requiring you to post in every forum and social channel:
- Status Pages → New Status Page
- Add your production monitors
- Link from your API README and docs
Since Bruno collections are in git, add the status page URL to your collection README:
# My API Collection
Bruno collection for the My API service.
- **Production status**: https://status.yourdomain.com
- **API docs**: https://docs.yourdomain.com
Anyone who clones your repo gets the status page link immediately.
What you've built
| What | How |
|---|---|
| Health endpoints | /health per service with dependency checks, in Bruno collection |
| External uptime monitoring | Vigilmon HTTP monitors per Bruno environment |
| Bru CLI heartbeats | Ping on CI success; missed ping triggers alert |
| Authenticated monitoring | Dedicated monitoring token for sensitive endpoints |
| Instant alerts | Slack / Discord / email with escalation |
| Public status page | Linked from git repo README |
Bruno's git-native design means your API collections, environments, and documentation live alongside your code. Your monitoring setup should be just as intentional — continuous, external, and automatic.
Get started free at vigilmon.online — your first monitor is running in under a minute.