ADR-015: Separate per-service (Tier-0) and fleet-wide (Tier-1) dashboards
Status: Accepted Date: 2026-04-21
Context
On-call triage and platform health are different questions asked by different people at different times.
- The service owner asks per-service questions: "is distill healthy?", "what error is my user hitting?", "did my deploy break anything?".
- The platform operator / on-call engineer asks fleet-wide questions: "what is on fire right now?", "is the log pipeline alive?", "is the redactor still catching secrets?".
A service-owner dashboard cluttered with platform signals slows service triage. A platform dashboard cluttered with per-service detail hides the "which service" question the platform operator is asking.
The cardinality also differs. Per-service dashboards scale with service count (one set of Tier-0 dashboards per service, instantiated by the scaffold). Platform dashboards are a fixed set, hand-maintained, one copy fleet-wide. Treating them as the same artefact would require the scaffold to know about "platform mode" as a special case, which is the same kind of coupling we avoided in ADR-013.
Decision
Grafana dashboards are partitioned into two tiers:
- Tier-0 (service): Per-service, automatically generated. One set of templates in
infrastructure/grafana-templates/service-*.json.tmpl; the scaffold CLI materialises them intoobservability/grafana/<service>/per service. Tagged["gyrum", "tier-0"]. UID suffix is the service slug. - Tier-1 (platform): Fleet-wide, hand-maintained. Single copies in
infrastructure/grafana-templates/platform-*.json. Not templated. Tagged["gyrum", "tier-1"]. UID is the platform-type slug, no service suffix.
A dashboard belongs to Tier-0 if its question names a service (or is answerable only about one service at a time). A dashboard belongs to Tier-1 if its question is about the fleet, or about the observability substrate itself (log pipeline health, redaction events, probe freshness).
The scaffold CLI only touches Tier-0 by default. gyrum-dashboard-scaffold --platform is the admin path for instantiating Tier-1; it copies without substitution.
Consequences
- Service-owner triage is uncluttered. The Tier-0 set (
service-overview,service-errors,service-resources,service-canary-compare,service-deploy-health) covers every per-service question. The service owner never needs to filter out fleet noise. - Platform operator has a single coherent surface.
platform-overviewlists every service;platform-incidentis the 3am view;platform-redaction-eventsandplatform-probe-freshnesscover the substrate. An on-call engineer can bookmark six dashboards and answer every fleet-level question from them. - Duplication is allowed by design.
service-overviewfor distill shows distill's error rate;platform-overviewshows every service's error rate. Same query, different scope. The duplication is not waste — the two dashboards answer different questions (service vs fleet) and both are valuable. - The scaffold stays simple. Scaffold logic reads Tier-0 templates from
service-*.json.tmpl, substitutes tokens, writes to the service repo. It does not need a "platform mode" — the--platformflag is a separate path that just copies files. The common case (a service developer running the scaffold) never touches Tier-1. - Per-tier authoring rules. Tier-0 templates ship in the Tier-0 set and change on a different cadence than Tier-1 platform dashboards. A new Tier-0 template requires editing the scaffold's default list; a new Tier-1 dashboard is a file add in the templates directory. Different cadences, different reviews.
- Tag-based Grafana folders. Grafana's folder organisation leans on the
tagsfield;tier-0andtier-1drive folder placement consistently across the fleet. See the scaffold output behaviour. - Tier-0 templates and Tier-1 platform dashboards don't cross-reference each other's UIDs. A platform dashboard that deep-linked into
service-overview-distillwould break for any service not yet scaffolded. Instead, Tier-1 dashboards show fleet rollups; the engineer clicks through to the specific service dashboard via a Grafana folder search.
Alternatives considered
- Single tier: everything is per-service, platform view is a Grafana variable. Rejected: "fleet-wide" isn't a service-filter value, and force-fitting it into a template variable hides the different author/cadence story behind a dropdown. Also, platform-incident's four-quadrant layout is materially different from a per-service overview; trying to make one template answer both is a worse version of both.
- Single tier: only platform dashboards, filter by service in every query. Rejected: per-service dashboards are how service owners reason about their service, and every filter-by-service query is a worse version of a dedicated dashboard for that service. Also, the per-service drill-down story (pasting a
user_idorvideo_idto reconstruct an incident) needs per-service templating for the drill-key variable — not something a fleet dashboard does well. - Three tiers: service, platform, infra. Separate infra (VPS-level) from platform (observability). Rejected as premature: we have one
platform-infradashboard; splitting a tier for one dashboard is structure-for-structure's-sake. Revisit if the platform-infra bucket grows past four or five dashboards. - Per-org tier. Useful if we multiplexed many customers' services on shared Grafana. Rejected: we don't, and won't for the foreseeable future. Single-org keeps the model simple.
Supersedes: none Superseded by: