
Supabase Free Plan Limits: How to Survive Disk & CPU Exceeded
Service is growing, but Supabase alerts 'Disk Full'? Understanding the real limits of the Free Tier and optimization tips to survive without upgrading.

Service is growing, but Supabase alerts 'Disk Full'? Understanding the real limits of the Free Tier and optimization tips to survive without upgrading.
Foundation of DB Design. Splitting tables to prevent Anomalies. 1NF, 2NF, 3NF explained simply.

Solving server waste at dawn and crashes at lunch. Understanding Auto Scaling vs Serverless through 'Taxi Dispatch' and 'Pizza Delivery' analogies. Plus, cost-saving tips using Spot Instances.

Optimizing by gut feeling made my app slower. Learn to use Performance profiler to find real bottlenecks and fix what matters.

Text to Binary (HTTP/2), TCP to UDP (HTTP/3). From single-file queueing to parallel processing. Google's QUIC protocol story.

I launched a toy project, and the community response was great. Watching users grow from 100 to 500 felt amazing.
But one morning, a terrifying email arrived from Supabase.
"Your project has exceeded its database disk size limit. Changing your project to read-only."
My DB exceeded 500MB and entered Read-only mode. Users complained, "Cannot login," "Cannot post." The service effectively shut down. "Wait, 500MB for just text storage? No way."
I only stored email/names in users and some articles in posts.
Text data is usually KB sized. 500MB seemed impossible.
"Is the database lying? Are logs piling up?"
I analyzed Storage Usage and found the culprits:
Crucially, I didn't know "Deleting data doesn't immediately free up disk space."
I understood it as "Moving Boxes."
I was deleting items but leaving the empty boxes cluttering the warehouse.
Supabase Free Tier (500MB) is small but lasts long if optimized.
Run this in SQL Editor to see who eats space.
SELECT
relname as Table,
pg_size_pretty(pg_total_relation_size(relid)) As Size,
pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) as External_Size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
For me, it was a logs table I created manually.
"Ah, I dumped all console.log into the DB..."
After massive DELETE, run VACUUM.
-- Light cleaning (No downtime, less space reclaimed)
VACUUM users;
-- Deep cleaning (Locks table, Reclaims all space)
VACUUM FULL users;
⚠️ Warning: VACUUM FULL locks the table. Do it during off-peak hours.
"Just in case" indexes consume huge disk space. Drop unused indexes.
DROP INDEX IF EXISTS idx_users_last_login;
PITR generates massive WAL logs. For free tier projects, standard daily backups might be enough. Check Project Settings -> Database -> Backups.
Disk isn't the only issue. CPU (Instance Size) is strict. Micro instance (Free) has 2 CPUs, 1GB RAM. A slight traffic spike or bad query sends CPU to 100%.
The main cause is "N+1 Query". Using ORM (Prisma) incorrectly can fire 100 queries for 1 user visit. Check Supabase Dashboard -> Query Performance. Optimize the "Most Time Consuming" queries. If you don't fix this, upgrading to the $25 Pro plan won't save you; it'll just crash later.
Many devs store "Request Logs" in a SQL table. RDBMS is not for logs. It's a waste of expensive SSD. Use Supabase Log Drains or external services (Sentry, LogFlare).
Serverless functions (Vercel) spin up and down in milliseconds. If each function creates a new DB connection, the DB CPU explodes just simply handling the "Handshakes". You get "Too many clients" error.
Supabase provides Port 6543 (Transaction Mode) and 5432 (Session Mode). For Serverless, ALWAYS use 6543.
# .env
# ❌ Session Mode (5432) - Will crash in serverless
DATABASE_URL="postgres://postgres:pw@db.bit.supabase.co:5432/postgres"
# ✅ Transaction Mode (6543) - Reuses connections efficiently
DATABASE_URL="postgres://postgres:pw@db.bit.supabase.co:6543/postgres"
If you miss this, Prisma will eat up all connection slots instantly.
A toy project getting featured on Reddit or Hacker News can send traffic 100x higher in minutes. This phenomenon is common enough to have a name: the Reddit Hug of Death. On the Free Tier, this scenario plays out predictably.
Here's what to do when it happens.
revalidate: 60 (ISR). Serve cached HTML for 1 minute intervals without hitting the DB at all.CPU drops back to 20%. "If the DB is dying, stop hitting it. Cache at the Edge." That's the principle that matters most under sudden high traffic.
Q: When should I upgrade to Pro ($25)? A:
Storage limit is 1GB. If users upload raw 10MB photos, you hit the limit in 100 uploads.
The Fix:
Supabase Pro offers Image Transformation.
For Free Tier: "Resize on Client."
Use browser-image-compression or Flutter's flutter_image_compress.
Shrink 10MB -> 200KB before it even leaves the user's device.
The deadliest limit in Free Tier is 200 Concurrent Realtime Connections. The 201st user gets blocked.
The Fix: Fallback to Polling Don't use Realtime for everything.
"Connection Limit" is the most confusing part of Free Tier. Supabase uses Supavisor, a scalable connection pooler.
If you use Serverless, this is Mandatory.
Check your Prisma/Drizzle config for ?pgbouncer=true.
This single change fixes 90% of "CPU 100%" issues.
VACUUM after delete, 3. Move logs out. Upgrade only after trying these.