Cloud Cost

Supabase billing explained: what you're actually paying for

Supabase has one of the better pricing pages in the developer tools space — but there's still a gap between what you understand when you sign up and what shows up on your invoice three months later. Storage egress. Realtime connections. Auth MAUs. Each one has a free tier that disappears faster than you'd expect.

This is a breakdown of every line item, what triggers it, and what to cut first when your bill starts growing.

The billing dimensions

Dimension Free tier Overage cost Surprise factor
Database size 500MB ~$0.125/GB Low
Storage 1GB $0.021/GB Low
Storage egress 2GB $0.09/GB High
Auth MAUs 50,000 $0.00325/MAU Medium
Edge function invocations 500,000/month $2 per 1M Medium
Realtime messages 2M/month $2.50 per 1M High
Realtime peak connections 200 $10 per 1,000 High

Storage egress: the biggest surprise

Most developers think about storage in terms of what they upload — but egress is what you download, and it's where bills spike unexpectedly.

Every time a user loads an image stored in Supabase Storage, that counts as egress. On the free plan you get 2GB — which sounds like a lot until you have a product page with 20 product images that loads 200 times a day. That's 20 × 200 = 4,000 image loads. At an average of 200KB per image, you're at 800MB just from that one page.

The fix: don't serve user-facing assets directly from Supabase Storage. Use a CDN in front of it, or move your static product images to Vercel's edge network where they're served for free. Keep Supabase Storage for user-uploaded files and private documents that don't need to scale globally.

Transformation API mulTiplies egress

If you're using Supabase's image transformation feature (resizing images on the fly), each transformed variant counts as a separate egress call. Cache transformed images aggressively or pre-generate the sizes you need.

Realtime: easy to forget, easy to overuse

Realtime is one of Supabase's most compelling features, but it's also the most common source of unexpected bills. Two things to watch:

Peak concurrent connections

The free tier allows 200 concurrent Realtime connections. That's not 200 per day — that's 200 at the same time. If you have a collaborative feature, a live dashboard, or push notifications, connections accumulate fast. A user with 3 browser tabs open counts as 3 connections.

Message volume

Every database change that flows through a Realtime subscription is a message. If you're subscribing to a high-frequency table (like analytics events, user activity logs, or chat messages), you can burn through 2M messages in hours.

Fixes:

// Clean up subscriptions properly
useEffect(() => {
 const channel = supabase
 .channel('messages')
 .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, handler)
 .subscribe()

 // This is critical — missing this is the #1 cause of connection leaks
 return () => supabase.removeChannel(channel)
}, [])

Auth MAUs: not what you think

MAU stands for Monthly Active User. In Supabase's definition, an MAU is any user who makes an authenticated request in a calendar month. A user who signs up in January but never logs in again doesn't count for February. That's the good news.

The less obvious part: if you're building a B2B app where users sign in once and stay signed in for months, your MAU count could be much higher than your "active" user count in the product sense. Every API call with a valid JWT counts.

At 50,000 free MAUs, most projects are fine until they grow. But if you're doing something like a public API where every caller authenticates, those MAUs add up.

Edge functions: usually not the issue

500,000 invocations per month is generous. Unless you're using edge functions for something that fires on every page load, you're unlikely to hit this limit. Monitor it but don't prioritize it.

Database size: the slow creep

500MB goes fast if you're storing large JSON objects, binary data in your database, or not cleaning up old records. Common culprits:

Run this to see your biggest tables:

SELECT
 schemaname,
 tablename,
 pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 20;

What to cut first

In order of impact for most projects:

  1. Move static assets off Supabase Storage — biggest egress reduction
  2. Audit Realtime subscriptions — fix missing cleanup, add filters
  3. Add egress caching for anything that stays in Storage
  4. Clean up the database — run the query above and delete what you don't need
  5. Check edge function frequency — are any firing too often?
Driftn Tip

Driftn's cloud cost audit connects to your Supabase account and identifies which of these you're actually hitting — storage egress trends, realtime connection counts, and database size growth — in about 30 seconds.

See your Supabase costs in 30 seconds

Driftn connects with a read-only token and shows exactly where your Supabase spend is going — and what to cut first.

Run a free audit →