Remote Work Tools

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

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):

Operations:

Team Health:

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:

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:

# 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

Built by theluckystrike — More at zovo.one ```