Supabase Pricing Explained: 2026 Breakdown
Chat2DB TeamSupabase invoices surprise people for a specific reason: the plan you choose is not what determines most of the bill. The plan sets a baseline, unlocks features and bundles some included quota. The actual number on the invoice is driven by per-project compute hours, disk, egress, storage, monthly active users and realtime traffic — each metered separately, and each capable of dominating the total on its own.
This guide explains the pricing model: the tier structure, what the compute add-on really is, every metering dimension that can generate a charge, how spend caps behave, and what makes a bill spike. It deliberately does not quote dollar amounts. Supabase revises its rate card regularly, so treat supabase.com/pricing (opens in a new tab) as the only source of truth for figures, and treat this article as the map of where those figures get applied.
Billing is per organization, not per project
The first structural fact to internalise: a plan is attached to an organization, not to a project. Every project inside an org inherits that org's plan, and the org receives a single invoice covering all of them.
That has three practical consequences:
- You cannot have one Pro project and one Free project in the same organization. If you want a genuinely free sandbox alongside a paid production app, it has to live in a separate org.
- Included quotas (egress, storage, MAU and so on) are pooled across the organization, not handed out per project. Ten small projects share one allowance.
- Per-project charges — compute and disk above all — are not pooled. They multiply. Ten projects means ten compute line items.
A very common cost mistake is spinning up a project per environment (dev, staging, preview, prod) inside a paid org and being surprised that the compute line quadrupled while the egress line barely moved.
The plan tiers
Free
Free projects run on the smallest compute size and carry hard usage ceilings rather than overage billing. The two constraints that bite in practice are the limit on how many active free projects an organization may have, and automatic pausing: a Free project with no activity for an extended period is paused and must be restored manually from the dashboard. Free is genuinely usable for prototypes, learning and demos, but it is not a tier you should build an on-call rotation around — there is no uptime commitment and no support path.
If you are searching for "supabase pricing free tier" expecting a permanently free production backend, the honest answer is that the free tier is a development tier. The moment you need daily backups, no pausing, or a compute size above the default, you are on Pro.
Pro
Pro is the standard paid tier and the one most teams land on. It adds a monthly base fee per organization, bundles a set of included usage quotas, enables daily backups with a retention window, removes project pausing, and typically includes a monthly compute credit that is applied against your organization's compute spend. That credit detail matters: on Pro, a single small project's compute is often fully or largely covered, and the incremental cost only starts once you add a second project, upgrade an instance size, or turn on read replicas.
Team
Team sits above Pro and is priced for organizations that need compliance and access-control features rather than more raw capacity: SOC 2 reporting, SSO for the dashboard, longer backup retention, higher-priority support and finer-grained project permissions. The usage meters work identically — you are buying governance, not gigabytes.
Enterprise
Enterprise is a custom contract: designated support, uptime SLAs, custom security reviews, BYO-cloud or dedicated arrangements, and negotiated rates. There is no self-serve price and no published rate card.
The compute add-on, explained properly
This is the single most misunderstood line item, and the one that most often explains a bill that looks too high.
Every Supabase project is backed by a dedicated Postgres instance, and that instance has a compute size — a t-shirt sizing ladder running from Micro at the bottom through Small, Medium, Large and on up to very large instances. Micro is the default on both Free and Pro. Anything above it is the "compute add-on".
Four properties are worth committing to memory:
- It is billed hourly, per project, for as long as the project exists. It is not usage-based in the serverless sense. An idle Pro project with zero requests still accrues compute hours every hour of the month. Cost stops when the project is deleted or paused, not when traffic stops.
- Size determines memory, CPU and — critically — the connection limits. Each size carries a maximum number of direct Postgres connections and a maximum number of pooler client connections. Running out of connections is one of the most common reasons teams are forced to upgrade, and it has nothing to do with how much data they store.
- Read replicas and branches are separate compute instances. Adding a read replica adds a full compute line item plus its own disk. Persistent preview branches do the same. This is where "we only have one project" quietly becomes four billable instances.
- Disk is billed independently of compute. Provisioned disk size, provisioned IOPS and provisioned throughput are three separate dials above an included baseline. A database that has grown past the included disk allocation generates a charge even if the instance itself stays on the same size.
The metering dimensions
Beyond compute and disk, these are the meters that can appear on an invoice. For each one, the question to ask is not "how much does it cost" but "what exactly increments the counter".
Database size. Measured as the space your data occupies on disk, which includes indexes, table bloat and TOAST storage — not the logical size of your rows. A 2 GB table with heavy update churn and no recent vacuum can occupy several times that on disk.
Egress (bandwidth). Unified across the whole platform: bytes leaving via the REST API, direct Postgres connections, Storage downloads, Realtime messages, Auth and Edge Functions all count toward one egress meter. For content-heavy apps this is frequently the largest usage line, and it is almost always Storage downloads that drive it.
Storage. Two meters: gigabytes stored in the object store, and image transformations, usually metered on the number of distinct origin images processed in the billing period rather than the number of transformed variants served.
Monthly active users (auth). The "supabase pricing auth" question. A user counts as active once per billing period if they have an authentication event in that period — a sign-in, a token refresh, a session validation. Counting is per user per period, not per login, so a user who signs in 400 times counts once. There are separate MAU meters for standard Supabase Auth, third-party auth (when you use an external identity provider such as Auth0, Cognito or Firebase in front of Supabase), and SSO/SAML users, each with its own included quota and rate.
The subtlety that catches people: silent token refreshes from a long-lived mobile session count as authentication events. An app that keeps users signed in indefinitely will report far more monthly active users than its "people who opened the app" analytics suggest.
Realtime. Two meters: peak concurrent connections and total messages delivered. The "supabase pricing realtime" trap is fan-out. One database write broadcast to 500 subscribed clients is 500 messages, not one. Postgres Changes subscriptions on a hot table multiply very quickly, and the resulting bytes also land on the egress meter.
Edge Functions. Metered on invocation count, and in current pricing also on execution time, with a free allowance per billing period.
Other line items. Point-in-time recovery, custom domains, IPv4 addresses, log drains, and extended log retention are each separately priced add-ons, generally per project per month.
Measure your own usage with SQL
The dashboard reports all of this, but the dashboard tells you the total after the fact. These queries tell you why the total is what it is, and you can run them today. Run them against your project from any client — psql, the SQL editor, or a desktop client such as Chat2DB (opens in a new tab) if you want to keep them as saved queries and re-run them each month.
How big is the database, really
SELECT pg_size_pretty(pg_database_size(current_database())) AS database_size;That is the number the database-size meter tracks. If it looks larger than the data you think you have, the next query explains the gap.
Which tables and indexes account for it
SELECT
n.nspname AS schema_name,
c.relname AS object_name,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,
pg_size_pretty(pg_relation_size(c.oid)) AS table_size,
pg_size_pretty(pg_indexes_size(c.oid)) AS index_size,
pg_size_pretty(
pg_total_relation_size(c.oid)
- pg_relation_size(c.oid)
- pg_indexes_size(c.oid)
) AS toast_size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'm', 'p')
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(c.oid) DESC
LIMIT 25;pg_total_relation_size is the one that matters for billing, because it includes indexes and TOAST. Two patterns show up constantly in Supabase projects: a logging or events table nobody ever prunes, and an index set that is larger than the table it serves.
Indexes that cost disk but earn nothing:
SELECT
schemaname,
relname AS table_name,
indexrelname AS index_name,
idx_scan AS times_used,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;Dead tuples that inflate disk without holding data:
SELECT
relname,
n_live_tup,
n_dead_tup,
CASE WHEN n_live_tup = 0 THEN 0
ELSE round(100.0 * n_dead_tup / n_live_tup, 1)
END AS dead_pct,
last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 10000
ORDER BY n_dead_tup DESC;Estimate your MAU before the invoice does
The auth.users table carries last_sign_in_at, which gives you a usable lower bound:
SELECT count(*) AS active_last_30_days
FROM auth.users
WHERE last_sign_in_at > now() - interval '30 days';Trend it by month to see growth:
SELECT
date_trunc('month', last_sign_in_at) AS month,
count(*) AS users_last_seen
FROM auth.users
WHERE last_sign_in_at IS NOT NULL
GROUP BY 1
ORDER BY 1 DESC
LIMIT 12;And check total signups versus genuinely active accounts, because the gap is often enormous:
SELECT
count(*) FILTER (WHERE last_sign_in_at > now() - interval '30 days') AS active_30d,
count(*) FILTER (WHERE last_sign_in_at > now() - interval '90 days') AS active_90d,
count(*) AS total_users
FROM auth.users;Treat these as an approximation, not a billing reconciliation. Supabase counts any authentication event, including refreshes that do not update last_sign_in_at, so your billed MAU will usually be somewhat higher than this figure. The value of the query is the trajectory: if active_30d doubles month over month, so will that line item.
How much are you storing in Storage
Object metadata lives in Postgres, so you can sum it directly:
SELECT
bucket_id,
count(*) AS object_count,
pg_size_pretty(sum((metadata->>'size')::bigint)) AS bucket_size
FROM storage.objects
GROUP BY bucket_id
ORDER BY sum((metadata->>'size')::bigint) DESC;Find the individual files most likely to drive egress:
SELECT
bucket_id,
name,
pg_size_pretty((metadata->>'size')::bigint) AS object_size,
created_at
FROM storage.objects
ORDER BY (metadata->>'size')::bigint DESC
LIMIT 20;A single 40 MB video in a public bucket served 5,000 times is 200 GB of egress. That arithmetic explains more surprise invoices than everything else in this article combined.
Which tables are broadcasting realtime messages
Realtime message volume is a function of which tables are in the supabase_realtime publication and how often they change:
SELECT schemaname, tablename
FROM pg_publication_tables
WHERE pubname = 'supabase_realtime'
ORDER BY schemaname, tablename;Cross-reference that against write volume per table:
SELECT
relname,
n_tup_ins AS inserts,
n_tup_upd AS updates,
n_tup_del AS deletes,
n_tup_ins + n_tup_upd + n_tup_del AS total_writes
FROM pg_stat_user_tables
ORDER BY total_writes DESC
LIMIT 20;If a high-write table appears in the publication, multiply its write count by your typical number of subscribed clients. That product is your realtime message meter.
From the CLI
The Supabase CLI ships inspection commands that wrap much of the above:
supabase link --project-ref your-project-ref
supabase inspect db db-stats --linked
supabase inspect db table-sizes --linked
supabase inspect db index-sizes --linked
supabase inspect db bloat --linked
supabase inspect db unused-indexes --linked
supabase inspect db cache-hit --linkedsupabase inspect db bloat is the one to run before you consider upgrading disk. Reclaiming bloat is free; provisioned disk is not.
Spend caps and what happens at the limit
On paid plans Supabase offers a spend cap, and it is on by default. Understanding its behaviour is essential:
- Spend cap on: you are billed the plan fee plus fixed add-ons, but usage beyond your included quotas is not billed. Instead the affected service is restricted. Exceeding included egress or database size in this mode can mean your project is throttled or put into a restricted state — your bill is predictable, your availability is not.
- Spend cap off: usage above the included quotas is billed at the published overage rates and service continues uninterrupted. Your availability is predictable, your bill is not.
Neither setting is correct in the abstract. A side project should keep the cap on. A production application with paying users should turn it off and rely on billing alerts instead, because an unexpected invoice is recoverable and an outage during a traffic spike is not. What you should never do is leave the cap on, assume it protects you, and discover during a launch that "protected" meant "restricted".
Set up budget notifications either way. Supabase surfaces a usage breakdown per meter in the org billing section; check it once a month at minimum, and always in the week after any launch.
What actually makes bills spike
In rough order of how often each one is the culprit:
- Public Storage buckets serving large media without a CDN in front. Egress is the usual number-one line item for content apps. Put a CDN in front of public assets, or serve via signed URLs from a CDN origin, and the meter drops sharply.
- Project sprawl. Each preview branch, staging environment and read replica is a full compute instance billed hourly. Delete branches when their pull request merges.
- Realtime fan-out on a hot table. Broadcasting every write on an
eventsorpresencetable to every connected client multiplies messages by subscriber count. Subscribe to filtered channels, or use Broadcast with explicit payloads instead of Postgres Changes on a high-churn table. - Long-lived sessions inflating MAU. If your token refresh interval keeps effectively dormant users "active", your auth meter reflects your install base rather than your engaged users.
- Unbounded log and audit tables. They inflate database size, they inflate backup size, and they never stop growing. Add a retention policy on day one.
- A compute upgrade made during an incident and never reverted. Instance sizes are easy to raise under pressure and easy to forget about afterwards.
Estimating a monthly bill for a small app
Because rates change, the useful deliverable is a worksheet rather than a total. Take a realistic small production app: one project, roughly 10,000 monthly active users, an 8 GB database, 40 GB in Storage, around 150 GB of monthly egress, modest realtime usage with a few hundred concurrent connections, and daily backups.
Fill this in against the current rate card:
| Line item | Your number | Billing basis | Included on Pro? |
|---|---|---|---|
| Plan fee | 1 organization | Flat monthly | — |
| Compute | 1 instance, size chosen | Per project, per hour | Partly, via compute credit |
| Disk | 8 GB plus IOPS/throughput | Per GB provisioned | Baseline included |
| Database size | 8 GB | Per GB above quota | Quota included |
| Egress | 150 GB | Per GB above quota | Quota included |
| Storage | 40 GB | Per GB above quota | Quota included |
| MAU (auth) | 10,000 | Per MAU above quota | Quota included |
| Realtime | peak connections and messages | Two separate meters | Quota included |
| Edge Functions | invocations and execution time | Per unit above quota | Quota included |
| Add-ons | PITR, custom domain, IPv4 | Flat per project | No |
The method is the same in every case: subtract the included quota, multiply the remainder by the published unit rate, add the flat fees. Do it once and you will discover which one or two meters dominate your particular shape of application — and those are the only ones worth optimising.
On yearly billing: if you are searching "supabase pricing yearly", note that the self-serve plans are billed monthly. Annual or committed-spend arrangements exist, but they are negotiated as part of an Enterprise contract rather than offered as a checkbox at checkout. Check the pricing page or talk to sales rather than assuming a standard annual discount applies.
Keeping costs predictable
- Separate free experiments into their own organization so they never inherit a paid plan.
- Delete preview branches and decommissioned projects; compute bills by the hour whether or not anyone is connected.
- Put a retention policy on every append-only table before it exists, not after it is 40 GB.
- Run the table-size and unused-index queries above monthly and reclaim space rather than buying disk.
- Front public Storage buckets with a CDN.
- Audit which tables are in the realtime publication every quarter.
- Decide deliberately whether your spend cap is on or off, and make sure everyone on the team knows which.
Supabase's pricing model is more transparent than most managed-database offerings — every meter is visible and every number is one you can measure yourself. The failure mode is not opacity, it is inattention: nobody looks at the meters until the invoice arrives. Pin the queries from this article somewhere you will actually re-run them, and check the current rates at supabase.com/pricing (opens in a new tab) before you make any sizing decision.
