What is churn rate, and how do you measure it in Metabase?
Churn rate is the share of recurring revenue — or customers — you lose in a period. It's the counterweight to growth: even fast acquisition can't outrun high churn. Measure it in Metabase from billing data synced into a database (Stripe, Chargebee, Paddle, or Recurly).
How is churn rate calculated?
Pick a period (usually a month) and a fixed starting cohort:
- Revenue churn rate = churned MRR in the period ÷ MRR at the start of the period.
- Customer churn rate = customers lost ÷ customers at the start.
Measure loss against the cohort that existed at the start of the period, not the end — otherwise new signups dilute the rate and hide the problem.
Revenue vs. customer churn — and voluntary vs. involuntary
- Revenue churn weights each loss by its MRR. Losing one large account can dwarf many small ones.
- Customer (logo) churn counts accounts equally. Good for product-market-fit signals.
- Voluntary churn — the customer chose to cancel. Fix with product and value.
- Involuntary churn — a payment failed. Fix with dunning and card-update flows; see failed-payment rate.
When expansion outweighs churn, net revenue retention exceeds 100% — track it alongside churn.
What data does churn rate need?
- Subscription start and cancellation/expiry timestamps.
- A cancellation reason or type to separate voluntary from involuntary.
- A monthly MRR model for revenue churn.
SQL patterns
Churned MRR against MRR at each month's start, from a monthly MRR model.
-- Requires a monthly MRR model: one row per subscription per month
WITH by_month AS (
SELECT
month,
SUM(mrr) AS mrr,
LAG(SUM(mrr)) OVER (ORDER BY month) AS mrr_start,
SUM(CASE WHEN is_churned THEN mrr_lost ELSE 0 END) AS churned_mrr
FROM modeled_mrr
GROUP BY month
)
SELECT
month,
ROUND(100.0 * churned_mrr / NULLIF(mrr_start, 0), 2) AS revenue_churn_pct
FROM by_month
ORDER BY month;Cancellations against the subscriptions active at each month's start.
WITH months AS (
SELECT generate_series(
date_trunc('month', CURRENT_DATE - INTERVAL '11 months'),
date_trunc('month', CURRENT_DATE),
INTERVAL '1 month'
) AS month
)
SELECT
m.month,
COUNT(*) FILTER (
WHERE s.created <= m.month
AND date_trunc('month', s.canceled_at) = m.month
) AS churned,
COUNT(*) FILTER (
WHERE s.created <= m.month
AND (s.canceled_at IS NULL OR s.canceled_at > m.month)
) AS active_at_start,
ROUND(100.0 * COUNT(*) FILTER (
WHERE s.created <= m.month
AND date_trunc('month', s.canceled_at) = m.month
) / NULLIF(COUNT(*) FILTER (
WHERE s.created <= m.month
AND (s.canceled_at IS NULL OR s.canceled_at > m.month)
), 0), 2) AS customer_churn_pct
FROM months m
CROSS JOIN subscriptions s
GROUP BY m.month
ORDER BY m.month;Pitfalls
Where this metric applies
- Stripe + Metabase — subscription
canceled_atand status - Chargebee + Metabase — subscription
cancelled_atand cancellation reason - Paddle + Metabase — subscription
canceled_atand scheduled changes - Recurly + Metabase — subscription
stateandexpires_at