brily
Guides

Monitors-as-code.

Past a certain team size — roughly four engineers — click-to-create monitors stops scaling. Monitors-as-code puts every monitor in source control so you can diff changes on pull requests, roll back regressions, and bootstrap new environments with a single command.

Install the CLI

# via npm
npm install -g @brily/cli

# via homebrew
brew install brily/tap/cli

# verify
brily --version
brily auth login   # opens a browser; pastes token locally

The manifest

Every monitor lives as a file under .brily/monitors/. YAML is the default; JSON works if you prefer. A minimum monitor:

# .brily/monitors/homepage.yml
kind: Monitor
name: homepage
url: https://example.com
method: GET
interval_seconds: 60
regions: [fra, hel, ams]
quorum: 2
assertions:
  - type: status
    operator: eq
    value: 200
  - type: body_contains
    value: Sign in
alert_policy:
  severity: p1
  slack_channel: "#ops-alerts"
  escalate_after_minutes: 10

Deploy from CLI

# preview what would change (read-only)
brily plan

# apply the diff
brily apply

# apply only specific monitors
brily apply --filter homepage

brily planis safe to run on any branch. It prints a unified-diff style comparison between your local manifests and what's live in the project.

Deploy from CI

Wire brily apply into your main-branch CI. Example for GitHub Actions:

name: Deploy monitors
on:
  push:
    branches: [main]
    paths: [.brily/**]

jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: brily/setup-cli@v1
      - run: brily apply
        env:
          BRILY_TOKEN: ${{ secrets.BRILY_TOKEN }}

Plan on pull requests

Add a PR-only workflow that runs brily plan and posts the output as a PR comment. Reviewers can see exactly what will change before approval.

- name: Plan monitors
  run: brily plan --format=markdown > plan.md
  env:
    BRILY_TOKEN: ${{ secrets.BRILY_TOKEN_READONLY }}

- uses: marocchino/sticky-pull-request-comment@v2
  with:
    header: brily-plan
    path: plan.md

Ownership and sharding

Put each domain's monitors in its own subdirectory and use CODEOWNERS to enforce review:

# .github/CODEOWNERS
.brily/monitors/checkout/  @acme/checkout-team
.brily/monitors/billing/   @acme/billing-team
.brily/monitors/web/       @acme/web-team

Mixing code and UI

You can manage some monitors in code and others in the UI — but we don't recommend it. The CLI has a --prune flag that deletes monitors not present in manifests, which will wipe UI-created monitors if enabled. Either go all-in on code (recommended) or keep them in the UI entirely.

Templating and composition

YAML anchors cover the 80% case. For anything more complex, we support Go templates:

{{- range .environments }}
---
kind: Monitor
name: api-health-{{ .name }}
url: {{ .api_base }}/health
interval_seconds: 60
regions: {{ .regions | toYaml | nindent 2 }}
{{- end }}

Context variables come from .brily/config.yml or environment variables with a BRILY_VAR_ prefix.

When to adopt this

  • Four engineers or more— the "who owns this monitor" problem starts appearing around here.
  • Multiple environments — staging and production monitors that share config are a copy-paste nightmare in the UI.
  • Compliance sensitivity — audit trails from git history are stronger than UI change logs for SOC 2 / ISO 27001.

If none of these apply, the UI is fine. Adoption can be incremental — export existing monitors to manifests with brily export, then wire up the CI job when you're ready.