Supabase ships solid default database monitoring for the first year of a project. Past a certain scale, three gaps become visible: there is no way to look at yesterday, the "slow queries" view is reactive, and the dashboard does not tell you which slow query hurt the user. This post describes the three gaps, what fills each one honestly, and where a single tool cannot.
What Supabase gives you by default
The built-in dashboard covers the basics, and it covers more of them than it did a year ago. Live CPU, memory, disk, and connection counts. A query-performance report powered by pg_stat_statements, now with an Indexes tab that runs index_advisor against the query you selected and proposes a concrete CREATE INDEX with estimated cost before and after. Security and performance advisors that run periodic checks against the schema. A Metrics API exposing roughly 200 Postgres health and performance metrics in Prometheus exposition format, refreshed every minute. Logs retained for the current plan's window.
This is the right starting set for a project that has not hit scale, and the gaps below are narrower than they were. They have not closed.
The friction appears when the team grows and the database becomes load-bearing. Three specific things the default dashboard cannot do well.
Gap 1: no historical context
The Supabase query-performance view shows current pg_stat_statements rows. It does not retain snapshots. If a query slowed down last Tuesday after a deploy, there is no way to see that from the dashboard today, because yesterday's counter values are gone. pg_stat_statements itself is a running tally; without periodic external snapshots, the past is invisible.
The operational consequence: regressions are only visible during the window in which they are actively happening. A query that degraded a week ago and is now stable at the worse performance level reads as "fine" on the dashboard. The new baseline is the only baseline the tool can see. Slow drift with no deploy attached, the classic signature of index bloat, is exactly the kind of regression a history-free view absorbs without comment.
The Metrics API does not by itself close this, and it is worth being precise about why, because it looks like it should. It is a live endpoint in Prometheus exposition format that refreshes every minute. That is exactly the shape a scraper wants, and it moves the job from "write and maintain a snapshot query against pg_stat_statements" to "point a Prometheus-compatible collector at a URL." Real simplification. But the scraping and the retention are still yours to run, and an endpoint that reports the present once a minute is not the dashboard remembering last Tuesday.
What fills this gap
Somewhere with longer retention than the live extension, fed on an interval. Three practical options, from least to most involved:
- A cron job that inserts
pg_stat_statementsinto a timestamped table inside the same database. Queryable with SQL, free, no new infrastructure. - A Prometheus-compatible collector pointed at the Metrics API, or an agent that reads the extension directly, shipping into a metrics store or warehouse (Prometheus, Grafana Mimir, ClickHouse, BigQuery). Keeps the production database clean, and the Metrics API means this no longer starts with writing a collector.
- An APM vendor with a Postgres integration. Buy-not-build, costs more, ties you to the vendor's query language.
The second option is also where a newer class of context tools is aimed: workload history kept as evidence next to the schema meaning, so a regression can be read against what the tables and metrics mean rather than against a bare counter.
Gap 2: the top-queries view is reactive
The dashboard shows you which queries are slow right now. It does not show you that the query was fine before this morning's deploy. Reading the current state of pg_stat_statements and acting on it is reactive by construction: you find out after users are already experiencing the slower query.
index_advisor, wired into the Indexes tab of that same report, makes the view prescriptive rather than merely descriptive: it proposes the index and shows the estimated cost on either side of it. That is a genuine improvement and it does not change the timing. The advisor reasons about a query it has already watched run in production, which means the regression shipped before the recommendation existed. Its documented scope is single-column B-tree indexes only, so the composite cases, which are usually the ones a filter-plus-sort query needs, still land on a human.
The shift that actually reduces on-call pages is moving detection from dashboard-time to deploy-time or, better, PR-time. A check that runs before a migration lands can flag common Postgres performance footguns from the DDL alone: missing index on a filter column, CREATE INDEX without CONCURRENTLY, ACCESS EXCLUSIVE DDL on a table with long-running readers. A check that compares pg_stat_statements call counts across a deploy boundary can flag call-count regressions that an N+1 refactor introduced, within minutes of the deploy, not hours.
What fills this gap
A check that reads the proposed migration against the live schema and flags specific patterns before it lands. Missing-index-on-filter and non-CONCURRENTLY index builds on large tables are deterministic, lock-conflict risk between a migration and in-flight readers is probabilistic. This gap is the one Datapace is aimed at: the context that makes such checks worth trusting, what the tables mean, what the workload actually does, and what AI may run against either.
Important honest limit: a pre-merge check cannot detect every performance regression. N+1 cascades from application loops are invisible to it, because the loop is in the app, not in the SQL. For those, the post-deploy pg_stat_statements diff is the only catch.
Gap 3: no correlation between database and user experience
The database dashboard tells you which queries are slow. It does not tell you which user-facing endpoint is degraded because of those slow queries, or how much money a slow query is costing per hour, or whether the slow query is on a path that matters.
A query that doubles in latency from 10 ms to 20 ms is uninteresting if it runs once a day on an internal admin page. The same change on the login path is a production incident. The database has no way to know which is which, because the database has no view of the call graph. The call graph lives in the application, specifically in its distributed tracing.
What fills this gap
Distributed tracing with database spans. The application emits spans for user-facing requests, each span records the database queries it issued, a trace aggregator (Datadog, Honeycomb, Tempo, Jaeger) joins them into an end-to-end view. This is the only mechanism that can answer "this query is slow, is the login path slow?" Any tool that tries to answer that question without looking at trace data is guessing.
A database-layer tool does not fill this gap, ours included. For the login-path question, the right answer is a tracing system, not a database tool.
Three gaps, three tools
Supabase built-in
- Covers
- Current database state: CPU, connections, live slow queries, schema advisor
- Does not
- Historical comparisons, PR-time checks, user-impact attribution
- Right for
- Projects under ~50 engineers without a dedicated DBA
Complement with
- Retention
- Warehouse-style pg_stat_statements snapshot (own cron, agent, or APM)
- Proactive
- CI check against the migration and live schema (a pre-merge gate)
- Business impact
- Distributed tracing with database spans (Datadog, Honeycomb, Tempo)
The right tool for each gap may not be one tool
The temptation when reading a piece like this is to look for a single replacement for the built-in dashboard. That is the wrong shape. Retention, proactive detection, and business-impact correlation are three different problems that decompose onto three different parts of the stack. A warehouse handles historical retention well and is terrible at proactive detection. A CI check handles proactive detection well and has no view of retention. A tracing system handles user-impact correlation well and knows nothing about the database's internal metrics.
What works in practice is picking the right tool per gap and letting each do its job. Datapace is building the context layer around the database: what the schema means, what the workload actually costs and touches, and a policy gate over what AI may do against it. In this stack it shows up in the middle gap, the proactive one. Keep the Supabase dashboard; what is missing under it is a shared, machine-readable account of what the data means and what may run against it.
A note on when to graduate
The signal that the default is no longer enough is not a specific engineer count or a revenue threshold. It is a specific feeling, which most teams recognize when they hit it: the dashboard feels reactive, an incident this week had a root cause the dashboard was never going to surface before the page fired, and the post-mortem reads "we should have caught this at PR time." That is the day to look at complements. Before that day, the built-in is fine. If that is the gap you are feeling, you can walk through your own database with us.
Sources
- Supabase documentation, Telemetry (formerly "Monitoring and troubleshooting")
- Supabase documentation, pg_stat_statements
- PostgreSQL documentation, Monitoring statistics