Skip to content

Fleet monitoring

A Grafana board for the state of your repo fleet — template drift, CI on the default branch, open pull requests, and the working copies on your machine — with Prometheus keeping the history and six alert rules on top.

One container, one file. Nothing is discovered behind your back: a repo is on the board because repos.yml names it — or names the folder you keep it in — and for no other reason.

What you need

Docker, and the gh CLI signed in — or any GitHub token that can read the repos you list.

The recipe

Write a repos.yml — one entry per repo you want on the board, or one entry for a folder of them:

repos:
  - path: ~/repos/jebel-quant/rhiza     # a checkout on this machine
  - folder: ~/repos/cvxgrp              # every checkout in a folder
  - repo: Jebel-Quant/actions           # monitored, but not cloned here

owner/name comes from each checkout's origin, so the path is all you write, and a folder: is every checkout inside it — one line for an org you keep whole. Then:

docker run -d --name jq-fleet \
  -p 127.0.0.1:3000:3000 \
  -v "$PWD/repos.yml:/config/repos.yml:ro" \
  -v "$HOME:/host:ro" \
  -v jq-fleet-data:/data \
  -e GITHUB_TOKEN="$(gh auth token)" \
  ghcr.io/jebel-quant/monitoring:latest

open http://localhost:3000/d/jq-fleet

That is the whole install. The dashboard, the datasource, the alert rules and the scrape config are baked into the image, so there is nothing to clone and nothing on your disk but repos.yml.

The board fills in within a minute or two — the GitHub panels first, the working-copy panels once the first scan of the mount completes.

The four flags

-v .../repos.yml:/config/repos.yml:ro Required. The fleet — see Configuration.
-v "$HOME:/host:ro" Your home directory, read-only, so ~/... in repos.yml resolves. Leave it out and the working-copy panels stay empty; everything the GitHub half reports still works.
-v jq-fleet-data:/data Prometheus history and Grafana's database. Leave it out and both start empty at every run.
-e GITHUB_TOKEN=... Needs repo and read:org. Without one GitHub allows 60 calls an hour, which is not a fleet.
-e GITLAB_TOKEN=... Only if the fleet has a GitLab repo in it. Needs read_api.

Publish the ports to 127.0.0.1 only

Anonymous read access is on, so the board opens without signing in and admin / admin is only for settings. Published on 0.0.0.0 the container would serve private repo names, PR titles and local branch names to anyone on the network with no password. This is built to run on one machine; putting it on the internet is not a supported path.

Where to go next

  • Configuration

    repos.yml and the environment, dropping and purging a repo, and the API budget.

  • What it watches

    The four subjects, the metrics behind them, and why each is shaped the way it is — including two bugs that made repos look green while they were red.

  • The dashboard

    Reading the tiles, editing the JSON, alerting, and four PromQL traps worth not re-introducing.

  • Day to day

    Why panels say No data, and what the sign-in button actually is.

If every panel says \"No data\", the machine was probably asleep

Docker pauses with it. The Data age tile says how stale things are, and the next scrape lands a few seconds after waking. See Day to day.