How to build Linear analytics dashboards in Metabase
Linear is where your team plans and ships work. Metabase is where you turn that activity into shared, trustworthy dashboards. This guide covers two complementary paths: a lightweight MCP + CLI route that pulls live data with the Linear MCP server and loads a CSV into Metabase with the Metabase CLI for quick analysis, and a durable pipeline route that syncs Linear into a database so you can build dashboards anyone can read.
How do you connect Linear to Metabase?
Most teams combine both routes: use the Linear MCP server and Metabase CLI route to pull live data and stand up a quick analysis, and the pipeline route for the dashboards people depend on.
Live data in, quick analysis out
Pair the official Linear MCP server (to look up live issues, cycles, and projects) with the Metabase CLI, whose upload command loads a CSV into Metabase as a ready-to-query table and model.
- Ad-hoc questions like "which projects slipped this cycle?"
- Loading a Linear CSV export into Metabase in seconds
- Spot-checks and one-off analyses without a warehouse
- Great for exploration, not governed velocity reporting
- Use a read-scoped Linear connection so analysis can't trigger writes
- CSV uploads are snapshots — refresh or move to the pipeline for history
Durable dashboards with history
Sync Linear into a database or warehouse with Airbyte, dlt, or the GraphQL API, then point Metabase at it.
- Dashboards the whole team relies on
- Historical trends (velocity over quarters, cycle time drift)
- Joining Linear with GitHub, deploys, or support data
- Requires a destination database and a sync to maintain
- Some connector sources may be community-maintained — verify
- You own the data model and refresh schedule
What can you analyze from Linear data in Metabase?
- Throughput / velocity — issues and points completed per cycle
- Cycle time and lead time — from start (or created) to done
- Work-in-progress and flow — how much is open and where it sits
- Project and initiative health — scope, progress, and slippage
- Bug vs. feature mix — where engineering time actually goes
- Estimation accuracy — estimated vs. actual effort over time
- Team and individual load — open and completed work distribution
- Backlog age and aging issues — what's getting stale
Which Linear dashboards should you build in Metabase?
Engineering velocity
Track how much the team ships each cycle and whether it's stable.
- Completed issues per cycle (bar)
- Completed estimate points per cycle (bar)
- Rolling 4-cycle average throughput (line)
- Carryover: started but not finished in-cycle (number + trend)
Cycle time & flow
Understand how long work takes and where it gets stuck.
- Median cycle time by week (line)
- Cycle time distribution / p50–p90 (histogram or table)
- Current WIP by state (bar)
- Aging WIP: open issues by days-in-progress bucket (table)
Project & initiative health
See whether projects are on track by scope and progress.
- Project completion % vs. target date (table)
- Scope change: issues added after project start (line)
- Projects at risk: past target with open issues (table)
- Initiative roll-up: progress across child projects (bar)
Quality & bug load
Watch bug inflow vs. resolution and where defects cluster.
- Bugs created vs. resolved per week (dual line)
- Open bug count by priority (bar)
- Median time-to-resolution for bugs (line)
- Bug share of total completed work (number)
How do you use the Linear MCP server with the Metabase CLI?
Pair the Linear MCP server with the Metabase CLI for fast, hands-on analysis. Linear hosts a first-party remote MCP server that looks up live issues, cycles, and projects; the Metabase CLI's upload command loads a CSV into Metabase and creates a ready-to-query table and model. For analysis, use a read-scoped connection.
Example workflow
- Ask the Linear MCP which projects slipped this cycle, or list high-priority bugs with no assignee.
- Export the issues, cycles, and projects you want to keep as CSVs.
- Run
mb upload csvto load them into Metabase as tables and models, then build questions and dashboards on top.
Be honest about the limits
- The Linear MCP is great for live lookups — not for scheduled or audited velocity reporting.
- A CSV upload is a point-in-time snapshot; velocity and flow history still need a warehouse sync, or refresh with
mb upload replace. - Use a read-scoped Linear connection so analysis can't trigger writes.
mb upload csvneeds an uploads database configured under Admin → Settings → Uploads.
How do you set up the Linear MCP server and the Metabase CLI?
Linear MCP official
- Endpoint
https://mcp.linear.app/mcp- Transport
- Remote (Streamable HTTP)
- Auth
- OAuth 2.1 (browser) — no API key, no local install
- Note
- The
/sseendpoint is deprecated.
Metabase CLIofficial
- Install
npm install -g @metabase/cli- Auth
mb auth login(browser OAuth on v62+, or an API key)- Load data
mb upload csv --file data.csv- Requires
- An uploads database (Admin → Settings → Uploads)
# Linear (remote, OAuth in browser)
claude mcp add --transport http linear https://mcp.linear.app/mcpClaude Desktop: add Linear as a remote/custom connector (Settings → Connectors) using the same URL; Claude handles the OAuth pop-up.
codex mcp add linear --url https://mcp.linear.app/mcpFirst remote MCP in Codex? Enable the rmcp client in ~/.codex/config.toml:
[features]
experimental_use_rmcp_client = true
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"{
"mcpServers": {
"linear": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp"]
}
}
}Clients with native remote support can use a "url" field instead of the mcp-remote bridge. On first connection the Linear server opens a browser window to authorize.
# Install the Metabase CLI
npm install -g @metabase/cli
# Log in (opens your browser; requires Metabase v62+)
mb auth login --url https://your-metabase.example.com
# Load a Linear CSV export — creates a table AND a model
mb upload csv --file linear-issues.csv --collection root
# Refresh that same table later from a new export
mb upload replace <table-id> --file linear-issues.csvThe Metabase CLI stores its credentials securely after mb auth login.
Can you generate a Linear dashboard with AI?
Yes. Use the prompt below with any assistant that can run the Linear MCP server and the Metabase CLI. It works end to end: if Linear tables already exist in Metabase it analyzes those; otherwise it pulls the data over the Linear MCP, loads it with mb upload csv, then builds the dashboard — skipping cards it has no data for.
Create a polished Metabase dashboard for Linear analytics.
Work end to end: get the data into Metabase if it isn't there yet, then build.
Goal: Help engineering and product leaders understand delivery health, cycle
progress, backlog risk, and bug/support load from Linear data.
Step 1 — Find or load the data:
- First, check what already exists in Metabase (search for Linear tables and
models). If durable Linear data is already present — synced from a warehouse or
uploaded earlier — use it and skip to Step 2.
- If nothing is there, pull it with the Linear MCP server using a read-scoped
connection: issues, teams, projects, cycles, workflow states, labels, users,
and issue history / status-change events if available. Write each result to a
CSV, then load it with the Metabase CLI — run "mb upload csv --file
<export>.csv" so each upload creates a table and a ready-to-query model. Use
"mb upload replace <table-id> --file <export>.csv" to refresh an existing table
instead of creating duplicates.
Step 2 — Inspect before querying:
Do not assume exact table names. Map the available raw tables into these
analytical concepts where possible: Issues, Teams, Projects, Cycles, Workflow
states, Labels, Users, and Issue history / status-change events if available.
Inspect the actual tables and column names first.
Important:
- Build on whatever data is present; don't claim Metabase connects natively to
Linear — it reads a database or CLI-uploaded tables.
- If status history is missing, do not calculate true cycle time, scope change,
time in status, or reopen rate. Use a caveat instead.
- Only build a card if its underlying column/metric exists in the data.
- A single CSV is a point-in-time snapshot: velocity, flow, and scope-change
trends need a warehouse sync or multiple uploads, so build trend cards only if
that history is present.
Dashboard title: Linear Delivery Health
Sections:
1. Executive summary (KPI cards): Open issues; Issues completed last 7 days;
Issues created last 7 days; Median cycle time (only if a reliable
started_at/status-history field exists); High-priority open bugs; Active
projects at risk (only if project target/status fields exist).
2. Delivery throughput: Completed issues by week; Created vs completed by week;
Completed issues by team; Completed issues by priority.
3. Cycle health: Current cycle planned vs completed; Incomplete issues by team
and cycle; Carryover rate (only if cycle timing/snapshots exist); Scope added
after cycle start (only if issue-cycle history/snapshots exist).
4. Project health: Active projects by status; Open issues by project and
workflow state; Projects with target dates in next 30 days; Past-due/blocked
projects (if fields exist).
5. Backlog and aging: Open backlog by team; Open issues by workflow state;
Oldest open issues table; Unassigned/untriaged issues.
6. Bug and support load: New bugs by week; Resolved bugs by week; Open
high-priority bugs by age; Bug/support issues by product area or label
(use labels to identify bugs/support/customer requests if available).
Filters: Team, Project, Cycle, Label, Priority, Workflow state, Assignee,
Date range.
Reuse the models Metabase auto-created from uploaded CSVs, or (for a warehouse)
create reusable models: modeled_linear_issues, modeled_linear_teams,
modeled_linear_projects, modeled_linear_cycles, modeled_linear_workflow_states,
modeled_linear_labels, and modeled_linear_issue_events (only if history exists).
Output: Build the dashboard if you have permission; otherwise provide the exact
questions, SQL, model definitions, and layout. Include caveats for any metric
that cannot be calculated from the available data. Keep it practical, dense,
and executive-readable. Avoid vanity metrics.How do you build the Linear → Metabase pipeline?
Every pipeline is the same four stages: extract from Linear's API, load into a database, model the raw tables into clean ones, and visualize in Metabase. You can assemble this with a managed connector or a free script you host yourself.
1. Extract and load (pick one)
- dlt (free, code) — an open-source Python library that handles pagination, schema, and incremental loads against Linear's GraphQL API. The lightest path to a maintainable, no-vendor sync.
- Linear GraphQL API (free, raw) — write your own script that paginates issues, cycles, and projects and upserts on a schedule. Most control, and an AI assistant can scaffold it in minutes.
- Airbyte — has a Linear source connector. Free if you self-host the open-source version; paid on Airbyte Cloud. Good when you want a UI and many sources at once.
- Fivetran (paid, managed) — does not offer a native Linear connector; use dlt, the API, or Airbyte instead.
2. Where to run it (hosting)
Writing the sync is the easy part now — the real question is where it runs on a schedule. From lowest to highest maintenance:
- GitHub Actions (cron) — built-in scheduler, encrypted secrets, and free minutes that cover daily or hourly syncs. The simplest "run my script every day" option.
- Serverless functions — Cloud Functions, AWS Lambda, or Modal on a timer. Generous free tiers; scales without a server to manage.
- Managed cron platforms — Railway or Render cron jobs for a few dollars a month, with logs and retries built in.
- An always-on VM + cron — full control, but you own uptime and patching.
3. Where to land it (destination)
- Neon / Supabase / self-hosted Postgres — simplest and cheapest, free tiers available, and Metabase works great with it. Start here.
- BigQuery / Snowflake / Redshift — reach for these once volumes grow or you're joining Linear with many other sources.
Notes
- Land raw tables first, then build clean models on top — don't report off raw JSON.
- Schedule syncs to match how fresh dashboards need to be (hourly is usually plenty).
- Capture state-change history if you want accurate cycle/lead time; raw snapshots lose transitions.
How should you model Linear data in Metabase?
Core tables
| Table | Grain | Key columns |
|---|---|---|
issues | one row per issue | id, identifier, team_id, project_id, cycle_id, state_type, priority, estimate, assignee_id, created_at, started_at, completed_at, canceled_at |
cycles | one row per cycle | id, team_id, number, starts_at, ends_at |
projects | one row per project | id, name, state, target_date, started_at, completed_at, lead_id |
teams | one row per team | id, name, key |
users | one row per member | id, name, email, active |
Modeling advice
- Add derived
cycle_time = completed_at - started_atandlead_time = completed_at - created_atin your model layer, not in every question. - Normalize state into a small
state_typeset (backlog/started/completed/canceled) so charts stay stable when workflow states change. - If you need historical WIP/flow, model an
issue_state_historytable from API change events; point-in-time snapshots are unreliable. - Treat labels as a bridge table (
issue_labels) so an issue can carry many labels without exploding rows. - Define "completed" once (
state_type = 'completed') and reuse it everywhere to avoid metric drift.
Which Linear metrics should you track in Metabase?
| Metric | Definition | Notes |
|---|---|---|
| Throughput (issues completed) | Count of issues with state_type = 'completed' in a period. | Simplest velocity signal; pair with points to avoid gaming by issue-splitting. |
| Velocity (points completed) | Sum of estimate for issues completed in a cycle. | Only meaningful if the team estimates consistently. |
| Cycle time | completed_at − started_at, usually a median. | Use median (p50) and p90; averages are skewed by outliers. |
| Lead time | completed_at − created_at. | Captures queue/backlog wait, not just active work. |
| Work in progress (WIP) | Count of issues with state_type = 'started' right now. | High WIP often correlates with longer cycle time. |
| Carryover rate | Share of issues started in a cycle but completed later. | Signals over-commitment or scope churn. |
| Bug ratio | Completed bug issues ÷ all completed issues. | Watch the trend, not a single number. |
| Estimation accuracy | Relationship between estimate and actual cycle time. | Look for systematic under/over-estimation, not per-issue noise. |
What SQL powers Linear dashboards in Metabase?
These assume the modeled tables above (PostgreSQL dialect). Adjust identifiers to match your warehouse.
Issues completed in each cycle, by team.
SELECT
t.name AS team,
c.number AS cycle,
c.starts_at,
COUNT(*) AS issues_completed,
SUM(i.estimate) AS points_completed
FROM issues i
JOIN cycles c ON c.id = i.cycle_id
JOIN teams t ON t.id = i.team_id
WHERE i.state_type = 'completed'
GROUP BY t.name, c.number, c.starts_at
ORDER BY c.starts_at;Flow trend using the median of completed_at − started_at.
SELECT
date_trunc('week', i.completed_at) AS week,
percentile_cont(0.5) WITHIN GROUP (
ORDER BY EXTRACT(EPOCH FROM (i.completed_at - i.started_at)) / 86400.0
) AS median_cycle_days
FROM issues i
WHERE i.state_type = 'completed'
AND i.started_at IS NOT NULL
GROUP BY 1
ORDER BY 1;Snapshot of open work right now.
SELECT
i.state AS state,
COUNT(*) AS open_issues
FROM issues i
WHERE i.state_type = 'started'
GROUP BY i.state
ORDER BY open_issues DESC;Projects past their target date with open issues remaining.
SELECT
p.name,
p.target_date,
COUNT(*) FILTER (WHERE i.state_type <> 'completed') AS open_issues,
COUNT(*) AS total_issues
FROM projects p
JOIN issues i ON i.project_id = p.id
WHERE p.target_date < CURRENT_DATE
AND p.state <> 'completed'
GROUP BY p.name, p.target_date
HAVING COUNT(*) FILTER (WHERE i.state_type <> 'completed') > 0
ORDER BY p.target_date;Quality trend; assumes a 'Bug' label via an issue_labels bridge table.
WITH bug_issues AS (
SELECT i.*
FROM issues i
JOIN issue_labels il ON il.issue_id = i.id
WHERE il.label = 'Bug'
)
SELECT
weeks.week,
COUNT(created.id) AS bugs_created,
COUNT(resolved.id) AS bugs_resolved
FROM (
SELECT generate_series(
date_trunc('week', CURRENT_DATE - INTERVAL '12 weeks'),
date_trunc('week', CURRENT_DATE),
INTERVAL '1 week'
) AS week
) weeks
LEFT JOIN bug_issues created
ON date_trunc('week', created.created_at) = weeks.week
LEFT JOIN bug_issues resolved
ON date_trunc('week', resolved.completed_at) = weeks.week
GROUP BY weeks.week
ORDER BY weeks.week;What are common mistakes when analyzing Linear in Metabase?
state_type and derived time metrics.state_type = 'canceled' from throughput and completion-rate denominators where appropriate.