Remote teams need dashboards that surface the right metrics without requiring everyone to dig through tools manually. This guide covers the best options for engineering metrics, ops dashboards, and business KPIs — with setup configs for each.
Table of Contents
- 5.
- What to Measure
- 1. Grafana (Best All-Around)
- 2. Metabase (Best for Non-Technical Teams)
- 3. GitHub Insights + Custom Dashboards
- 4. LinearB (Purpose-Built Engineering Metrics)
- 5. Custom Prometheus + Grafana DORA Stack
- Dashboard Layout for Remote Teams
- 6. Making Dashboards Actually Useful for Remote Teams
- Related Reading
| Tool | Key Feature | Remote Team Fit | Integration | Pricing |
|---|---|---|---|---|
| Notion | All-in-one workspace | Async docs and databases | API, Slack, Zapier | $8/user/month |
| Slack | Real-time team messaging | Channels, threads, huddles | 2,600+ apps | $7.25/user/month |
| Linear | Fast project management | Keyboard-driven, cycles | GitHub, Slack, Figma | $8/user/month |
| Loom | Async video messaging | Record and share anywhere | Slack, Notion, GitHub | $12.50/user/month |
| 1Password | Team password management | Shared vaults, SSO | Browser, CLI, SCIM | $7.99/user/month |
Slack integration: daily digest
LinearB Settings > Notifications > Daily Digest > #engineering-metrics
## 5.
- **Topics covered**: what to measure, 1. grafana (best all-around), 2. metabase (best for non-technical teams)
- **Practical guidance included**: Step-by-step setup and configuration instructions
## What to Measure
Before picking tools, define your metric categories:
Engineering (DORA):
- Deployment frequency
- Lead time for changes (commit to production)
- Change failure rate
- Mean time to recovery (MTTR)
Operations:
- Service uptime / error rate
- P95 response time
- Infrastructure cost per service
Team Health:
- PR cycle time (open to merge)
- PR review turnaround
- Incidents per week
- Time in meetings vs deep work ```
1. Grafana (Best All-Around)
Cost: Free (self-hosted), $8/user/month (Cloud) Best for: Infrastructure, app metrics, mixed data sources
Deploy with Docker:
# docker-compose.yml
services:
grafana:
image: grafana/grafana:10.3.1
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
- GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-worldmap-panel
- GF_AUTH_GENERIC_OAUTH_ENABLED=true
- GF_AUTH_GENERIC_OAUTH_NAME=SSO
- GF_AUTH_GENERIC_OAUTH_CLIENT_ID=grafana
- GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=${OAUTH_SECRET}
- GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://auth.example.com/realms/company/protocol/openid-connect/auth
- GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://auth.example.com/realms/company/protocol/openid-connect/token
- GF_AUTH_GENERIC_OAUTH_API_URL=https://auth.example.com/realms/company/protocol/openid-connect/userinfo
volumes:
- grafana_data:/var/lib/grafana
- ./dashboards:/var/lib/grafana/dashboards
restart: unless-stopped
DORA metrics dashboard using Prometheus:
# prometheus.yml - scrape GitHub Actions metrics
scrape_configs:
- job_name: 'github-actions-exporter'
static_configs:
- targets: ['github-actions-exporter:9101']
// Grafana dashboard panel for deployment frequency
{
"title": "Deployment Frequency (last 30d)",
"type": "stat",
"targets": [{
"expr": "count_over_time(deployment_total{environment=\"production\"}[30d])",
"legendFormat": "Deployments"
}],
"options": {
"reduceOptions": {"calcs": ["sum"]},
"colorMode": "background"
},
"fieldConfig": {
"defaults": {
"thresholds": {
"steps": [
{"value": 0, "color": "red"},
{"value": 4, "color": "yellow"},
{"value": 14, "color": "green"}
]
}
}
}
}
2. Metabase (Best for Non-Technical Teams)
Cost: Free (self-hosted), $500/month (Cloud) Best for: Business KPIs, SQL-based dashboards, stakeholder sharing
# Docker deployment
docker run -d \
--name metabase \
-p 3001:3000 \
-e MB_DB_TYPE=postgres \
-e MB_DB_DBNAME=metabase \
-e MB_DB_PORT=5432 \
-e MB_DB_USER=metabase \
-e MB_DB_PASS=password \
-e MB_DB_HOST=your-db-host \
--restart unless-stopped \
metabase/metabase:latest
SQL query for PR cycle time dashboard:
-- Average PR cycle time per week
SELECT
date_trunc('week', created_at) AS week,
round(avg(
extract(epoch from (merged_at - created_at)) / 3600
)::numeric, 1) AS avg_hours_to_merge,
count(*) AS prs_merged
FROM pull_requests
WHERE
merged_at IS NOT NULL
AND created_at > now() - interval '90 days'
GROUP BY 1
ORDER BY 1;
3. GitHub Insights + Custom Dashboards
GitHub’s built-in insights miss DORA metrics. Augment with gh CLI scripts:
#!/bin/bash
# scripts/dora-report.sh
# Generate DORA metrics from GitHub API
ORG="your-org"
REPO="your-repo"
SINCE=$(date -d "-30 days" --iso-8601)
echo "=== DORA Metrics: last 30 days ==="
# Deployment Frequency
DEPLOYS=$(gh run list \
--repo "$ORG/$REPO" \
--workflow deploy.yml \
--status success \
--created ">$SINCE" \
--json conclusion,createdAt \
--jq 'length')
echo "Deployment frequency: $DEPLOYS deployments ($(echo "scale=1; $DEPLOYS / 30" | bc)/day)"
# Lead time for changes
echo ""
echo "Lead Time (last 10 PRs):"
gh pr list \
--repo "$ORG/$REPO" \
--state merged \
--limit 10 \
--json createdAt,mergedAt,title \
--jq '.[] | {
title: .title,
hours: ((.mergedAt | fromdateiso8601) - (.createdAt | fromdateiso8601)) / 3600 | round
}' | jq -r '" PR: \(.title[:50]) — \(.hours)h"'
# Change failure rate
FAILED=$(gh run list \
--repo "$ORG/$REPO" \
--workflow deploy.yml \
--status failure \
--created ">$SINCE" \
--json conclusion \
--jq 'length')
TOTAL=$((DEPLOYS + FAILED))
echo ""
CFR=$(echo "scale=1; $FAILED * 100 / $TOTAL" | bc)
echo "Change failure rate: ${CFR}% ($FAILED failures / $TOTAL total)"
4. LinearB (Purpose-Built Engineering Metrics)
Cost: Free tier available, ~$15/user/month Best for: DORA metrics without building your own
LinearB connects to GitHub/GitLab and surfaces:
- PR cycle time breakdown (time to first review, review time, time to merge)
- Deployment frequency by team
- Work in progress limits
- Alerts when PRs are stale
Setup:
# Connect via LinearB dashboard (no self-hosting needed)
# 1. Connect GitHub org
# 2. Map repos to teams
# 3. Set targets: deployment frequency > daily, lead time < 48h
# Slack integration: daily digest
# LinearB Settings > Notifications > Daily Digest > #engineering-metrics
5. Custom Prometheus + Grafana DORA Stack
For full control, expose deployment events as Prometheus metrics:
# deploy_metrics.py - push gateway for deployment events
from prometheus_client import CollectorRegistry, Counter, push_to_gateway
import time
import os
registry = CollectorRegistry()
deployments = Counter(
'deployment_total',
'Total deployments',
['service', 'environment', 'status'],
registry=registry
)
def record_deployment(service: str, environment: str, status: str):
deployments.labels(
service=service,
environment=environment,
status=status
).inc()
push_to_gateway(
os.environ['PUSHGATEWAY_URL'],
job='deployments',
registry=registry
)
# Call at end of CI/CD pipeline:
record_deployment(
service=os.environ['SERVICE_NAME'],
environment=os.environ['ENVIRONMENT'],
status='success' # or 'failure'
)
# Add to GitHub Actions deploy workflow
- name: Record deployment metric
run: python scripts/deploy_metrics.py
if: always()
env:
PUSHGATEWAY_URL: https://pushgateway.example.com
SERVICE_NAME: my-service
ENVIRONMENT: production
Grafana alert for deployment frequency drop:
# grafana/alerts/dora.yml
groups:
- name: dora
rules:
- alert: LowDeploymentFrequency
expr: |
increase(deployment_total{environment="production",status="success"}[7d]) < 3
for: 1d
labels:
severity: warning
annotations:
summary: "Less than 3 production deployments this week"
Dashboard Layout for Remote Teams
Weekly team metrics page structure:
Row 1: DORA Overview (4 stats)
- Deployment Frequency (this week vs last week)
- Lead Time (median, last 30 PRs)
- Change Failure Rate (last 30 days %)
- MTTR (avg incident resolution time)
Row 2: Current Sprint
- Burndown chart
- PR queue depth
- Stale PRs > 2 days
Row 3: Service Health
- Error rate by service (time series)
- P95 latency by service
- Active incidents
6. Making Dashboards Actually Useful for Remote Teams
The biggest mistake engineering teams make is building dashboards no one looks at. Metrics become valuable when they’re embedded into existing rituals, not treated as a separate reporting layer.
Async Weekly Digest
Instead of expecting engineers to open Grafana each morning, push a digest to Slack automatically:
# scripts/weekly_digest.py
import requests
import os
from datetime import datetime, timedelta
SLACK_WEBHOOK = os.environ['SLACK_WEBHOOK_URL']
GRAFANA_URL = os.environ['GRAFANA_URL']
GRAFANA_TOKEN = os.environ['GRAFANA_TOKEN']
def fetch_metric(query: str, time_range: str = "7d") -> float:
resp = requests.get(
f"{GRAFANA_URL}/api/datasources/proxy/1/api/v1/query",
params={"query": query},
headers={"Authorization": f"Bearer {GRAFANA_TOKEN}"}
)
data = resp.json()
return float(data["data"]["result"][0]["value"][1])
def post_digest():
deploy_freq = fetch_metric(
'sum(increase(deployment_total{environment="production",status="success"}[7d]))'
)
lead_time = fetch_metric(
'avg(pr_lead_time_hours)'
)
failure_rate = fetch_metric(
'rate(deployment_total{status="failure"}[7d]) / rate(deployment_total[7d]) * 100'
)
color = "good" if deploy_freq >= 5 else "warning" if deploy_freq >= 2 else "danger"
payload = {
"attachments": [{
"color": color,
"title": f"Engineering Metrics — Week of {datetime.now().strftime('%b %d')}",
"fields": [
{"title": "Deploy Frequency", "value": f"{deploy_freq:.0f} this week", "short": True},
{"title": "Avg Lead Time", "value": f"{lead_time:.1f}h", "short": True},
{"title": "Change Failure Rate", "value": f"{failure_rate:.1f}%", "short": True},
]
}]
}
requests.post(SLACK_WEBHOOK, json=payload)
post_digest()
Schedule this with a GitHub Actions cron:
# .github/workflows/metrics-digest.yml
name: Weekly Metrics Digest
on:
schedule:
- cron: '0 9 * * MON' # Monday 9am UTC
jobs:
digest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install requests
- run: python scripts/weekly_digest.py
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
GRAFANA_URL: ${{ secrets.GRAFANA_URL }}
GRAFANA_TOKEN: ${{ secrets.GRAFANA_TOKEN }}
Defining Targets and Thresholds
Raw numbers without context create anxiety, not insight. Define team-specific targets before you publish dashboards publicly:
# team-metrics-targets.yml
dora:
deployment_frequency:
elite: ">= 1/day"
high: ">= 1/week"
medium: ">= 1/month"
current_target: high
lead_time_hours:
elite: "< 24"
high: "< 168" # 1 week
medium: "< 720" # 1 month
current_target: 48
change_failure_rate_pct:
elite: "< 5"
high: "< 10"
current_target: 10
mttr_hours:
elite: "< 1"
high: "< 24"
current_target: 4
team_health:
pr_review_turnaround_hours: 24
stale_pr_threshold_days: 3
meeting_hours_per_week_max: 10
Store this in your repo and reference it when configuring alert thresholds in Grafana. This makes targets a team decision rather than a tool default.
Dashboard Access Control for Remote Teams
With engineers spread across timezones, dashboard access needs to be frictionless:
- Use SSO: Configure Grafana’s OAuth so any team member logs in with their Google or GitHub account — no separate password management
- Public dashboards for execs: Grafana Cloud supports public dashboard URLs with read-only access; send leadership a static link rather than creating accounts for them
- Snapshot for async review: Use Grafana’s built-in snapshot feature (
Share > Snapshot) to capture point-in-time metrics for incident retrospectives or sprint reviews — snapshots are immutable and shareable without auth
# Create a Grafana snapshot via API
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $GRAFANA_TOKEN" \
-d '{
"dashboard": {"id": 5, "title": "DORA Metrics"},
"expires": 86400
}' \
"$GRAFANA_URL/api/snapshots"
# Returns a public URL valid for 24 hours
Related Reading
- Setting Up Loki for Remote Log Aggregation
- Setting Up Jaeger for Distributed Tracing
- Best Observability Platform for Remote Teams
- Best Analytics Dashboard for a Remote Growth Team of 4
Related Articles
- Best API Tools for Automating Remote Team Compliance
- Best Analytics Dashboard for a Remote Growth Team of 4
- Best Remote Work Tools for Java Teams Migrating from
- Best Business Intelligence Tool for Small Remote Teams
- Best Tool for Remote Teams Recording and Transcribing
Built by theluckystrike — More at zovo.one ```