What Is Supabase and Why Use It?
Supabase is an open-source Firebase alternative that bundles everything you need for a production backend into a single platform: PostgreSQL database, authentication, instant REST and GraphQL APIs, real-time subscriptions, file storage, and edge functions — all managed for you.
It launched in 2020 and has since become the default backend for tens of thousands of developers. With a 4.7/5 rating from 72+ verified reviews on G2 and a passionate community on GitHub (60K+ stars), it’s widely considered the most complete open-source backend platform available.
Why does everyone flock to it? Because instead of stitching together separate services for your database, auth, file uploads, and WebSocket connections, Supabase gives you all of them in one dashboard with a shared security model (Row-Level Security on Postgres).
The philosophy is simple: Postgres is the most powerful and battle-tested database in the world. Supabase wraps it with modern, developer-friendly tooling — auto-generated APIs, real-time listeners, a dashboard UI — and lets you skip server management entirely.
Why the Free Tier Matters
The Supabase free tier is not a time-limited trial. It’s a genuinely usable, no-expiration plan that can serve small-to-medium production apps indefinitely. Many developers have built side projects, MVPs, and even businesses that ran on the free plan for over a year before needing to upgrade.
As one developer building a SaaS on the free tier shared:
“I founded my business on the free tier and didn’t upgrade to the $25/mo plan until we hit 40K MAU. It bought me over a year of zero backend costs.”
That kind of runway changes your calculus when bootstrapping. G2 reviewers consistently highlight Supabase’s ease of setup, generous free tier, and PostgreSQL reliability as top reasons they chose it over alternatives — with many noting it went from prototype to production without requiring a credit card or a single infrastructure decision.
Supabase Free Tier: Key Features and Limits
Here is exactly what you get on the free plan as of July 2026:
| Resource | Free Tier Limit |
|---|---|
| PostgreSQL Database | 500 MB |
| File Storage | 1 GB |
| Egress (uncached) | 5 GB / month |
| CDN Cached Egress | 5 GB / month |
| Monthly Active Users (Auth) | 50,000 |
| API Requests | Unlimited |
| Realtime Concurrent Connections | 200 |
| Realtime Messages | 2 million / month |
| Realtime Max Message Size | 256 KB |
| Edge Function Invocations | 500,000 / month |
| Active Projects | 2 |
| Compute | Shared CPU, 500 MB RAM |
| Database Backups | ❌ Not included |
| Auto-pause | After 7 days of inactivity |
What’s NOT on the Free Tier
- No database backups — you need to pg_dump manually.
- No point-in-time recovery — that requires Pro ($25/mo) or higher.
- No SSO / SAML — Team plan ($599/mo) and above.
- No SLA — free tier is best-effort.
- No Branching — unlike Neon, Supabase doesn’t offer database branching.
- Shared compute — your database runs on shared CPU resources. Heavy traffic spikes can feel slow.
The 7-Day Auto-Pause Gotcha
Free projects with no database activity for 7 consecutive days are automatically paused. When paused, the project is inaccessible — your API, auth, and storage all go offline. To resume, just log into the Supabase dashboard and unpause it (takes about 30 seconds).
This doesn’t mean you need daily traffic. A single cron job hitting your DB once a week is enough to keep it alive. Or, if you know you’re going to be away, simply acknowledge that you’ll need to unpause when you return.
Best Use Cases With Real Examples
1. Real-Time Chat Application
Supabase’s Realtime feature uses PostgreSQL’s built-in replication to stream database changes to connected clients via WebSockets. This makes building a chat app almost trivial.
Real scenario: You’re building a customer support widget. Using Supabase, you create a messages table with RLS policies ensuring users can only see their own conversations. The frontend subscribes to changes:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
// Subscribe to new messages in a specific conversation
const channel = supabase
.channel('conversation-123')
.on(
'postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages', filter: 'conversation_id=eq.123' },
(payload) => {
// Append new message to UI in real-time
appendMessage(payload.new)
}
)
.subscribe()
On the free tier, 200 concurrent connections cover a support widget with moderate usage. Messages are limited to 256 KB each, which is more than enough for text.
2. Mobile App Backend with Auto-Generated TypeScript Types
One of Supabase’s most-loved features is its type generator. It introspects your Postgres schema and generates TypeScript definitions you can import directly into your frontend.
Real scenario: You’re building a React Native app with a profiles table, posts table, and likes join table. Instead of manually maintaining types:
npx supabase gen types typescript --project-id your-project-id > types/supabase.ts
Now your frontend has full type safety:
import { createClient } from '@supabase/supabase-js'
import type { Database } from './types/supabase'
const supabase = createClient<Database>(SUPABASE_URL, SUPABASE_ANON_KEY)
const { data: posts } = await supabase
.from('posts')
.select('id, title, author:profiles(username), likes_count')
.order('created_at', { ascending: false })
// ^ TypeScript knows the exact shape of `posts`
With 50,000 MAUs on the free tier, you can comfortably support a social app during beta without paying a cent for auth.
3. Content Management System with Row-Level Security
Supabase’s RLS policies let you define per-row access control directly in Postgres. This makes multi-tenant apps secure by default.
Real scenario: A SaaS where each organization has its own data. A single projects table serves all tenants, but RLS ensures Org A can never see Org B’s rows:
-- Enable RLS
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
-- Policy: users can only see projects in their organization
CREATE POLICY "Users can view their org's projects"
ON projects
FOR SELECT
USING (
organization_id IN (
SELECT organization_id FROM members
WHERE user_id = auth.uid()
)
);
The 500 MB database is enough for thousands of small-to-medium project records with associated metadata. Storage at 1 GB handles user uploads for profile pictures and document attachments.
4. Edge Functions for Lightweight API Endpoints
Supabase Edge Functions run on Deno and are deployed via the Supabase CLI. On the free tier, you get 500,000 invocations per month — enough for webhook handlers, Stripe payment confirmation processing, or image optimization tasks.
// supabase/functions/stripe-webhook/index.ts
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve(async (req) => {
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
const { record } = await req.json()
// Update user's subscription status
await supabase
.from('profiles')
.update({ plan: record.plan, status: record.status })
.eq('stripe_customer_id', record.customer)
return new Response('ok', { status: 200 })
})
Step-by-Step Setup Guide
Let’s build a working Supabase project from scratch — a todo list with authentication, a database table, and real-time updates.
Step 1: Create a Supabase Account and Project
- Go to supabase.com and click Start your project.
- Sign in with GitHub, GitLab, Google, or email.
- You’ll land in the Supabase Dashboard. Click New project.
- Fill in the details:
- Name:
my-todo-app(or whatever you like) - Database Password: Supabase generates a strong one — save it.
- Region: Pick the closest to your users (e.g.,
us-east-1oreu-west-2). - Pricing Plan: Select Free.
- Name:
- Click Create new project. It takes 30–60 seconds to provision.
No credit card required. You have 2 free projects available.
Step 2: Grab Your API Credentials
Once the project is ready, go to Project Settings → API in the sidebar. You’ll find two critical values:
- Project URL (e.g.,
https://your-project.supabase.co) - anon public key (a long JWT string)
These are all you need to connect from the frontend. Keep the service_role key secret — it bypasses RLS.
Step 3: Create a Table Using the SQL Editor
Go to SQL Editor in the sidebar and paste:
-- Create a todos table with Row Level Security
CREATE TABLE todos (
id BIGSERIAL PRIMARY KEY,
user_id UUID NOT NULL DEFAULT auth.uid(),
title TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
-- Users can only see their own todos
CREATE POLICY "Users can view their own todos"
ON todos FOR SELECT
USING (auth.uid() = user_id);
-- Users can insert their own todos
CREATE POLICY "Users can create their own todos"
ON todos FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Users can update their own todos
CREATE POLICY "Users can update their own todos"
ON todos FOR UPDATE
USING (auth.uid() = user_id);
-- Users can delete their own todos
CREATE POLICY "Users can delete their own todos"
ON todos FOR DELETE
USING (auth.uid() = user_id);
Click Run. Supabase automatically generates a REST API and TypeScript types for this table.
Step 4: Set Up Authentication
- Go to Authentication → Providers in the sidebar.
- Enable Email (for email/password login) and/or any social providers (Google, GitHub, GitHub, Discord, etc.).
- For Google/GitHub OAuth, you’ll need to get client ID and secret from those providers’ developer consoles and paste them in.
Supabase handles JWT generation, session management, and password hashing out of the box.
Step 5: Connect Your Frontend
Create a new React project (or use your existing one):
npm create vite@latest my-todo-app -- --template react-ts
cd my-todo-app
npm install @supabase/supabase-js
Create the Supabase client:
// src/supabase.ts
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
Add your .env file:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key-here
Step 6: Sign In and Fetch Data
// src/App.tsx
import { useEffect, useState } from 'react'
import { supabase } from './supabase'
type Todo = {
id: number
title: string
completed: boolean
created_at: string
}
function App() {
const [todos, setTodos] = useState<Todo[]>([])
const [user, setUser] = useState<any>(null)
useEffect(() => {
// Check current session
supabase.auth.getSession().then(({ data }) => {
setUser(data.session?.user ?? null)
})
// Listen for auth changes
const { data: listener } = supabase.auth.onAuthStateChange((_event, session) => {
setUser(session?.user ?? null)
})
return () => listener.subscription.unsubscribe()
}, [])
useEffect(() => {
if (!user) return
// Fetch todos for the current user
supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false })
.then(({ data }) => {
if (data) setTodos(data)
})
}, [user])
const signInWithGitHub = async () => {
await supabase.auth.signInWithOAuth({ provider: 'github' })
}
const signOut = async () => {
await supabase.auth.signOut()
setTodos([])
}
if (!user) {
return <button onClick={signInWithGitHub}>Sign in with GitHub</button>
}
return (
<div>
<h1>Hello {user.email}</h1>
<button onClick={signOut}>Sign Out</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={async () => {
await supabase
.from('todos')
.update({ completed: !todo.completed })
.eq('id', todo.id)
}}
/>
{todo.title}
</li>
))}
</ul>
</div>
)
}
export default App
Step 7: Add Realtime Subscriptions
To see new todos appear instantly when another client adds them, subscribe to changes:
useEffect(() => {
if (!user) return
const channel = supabase
.channel('todos-changes')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'todos', filter: `user_id=eq.${user.id}` },
() => {
// Refetch todos when any change happens
supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false })
.then(({ data }) => {
if (data) setTodos(data)
})
}
)
.subscribe()
return () => { supabase.removeChannel(channel) }
}, [user])
Your app now has authentication, a Postgres database, REST API, and real-time updates — all on the free tier.
How Supabase Compares to Alternatives
Supabase vs. Firebase
Firebase (Google) is the original BaaS — it pioneered the model Supabase follows. But the underlying technology differs fundamentally.
| Feature | Supabase (Free) | Firebase (Spark Plan) |
|---|---|---|
| Database | PostgreSQL (relational, SQL) | Firestore (NoSQL, document) |
| Database Storage | 500 MB | 1 GiB |
| Auth MAUs | 50,000 | 50,000 (Identity Platform) |
| Realtime | Postgres replication + WebSockets | Native real-time listeners |
| File Storage | 1 GB | 5 GB (Cloud Storage) |
| Egress | 5 GB + 5 GB CDN cached | 10 GB (varies by product) |
| Cloud Functions | Edge Functions (Deno) | Cloud Functions (Node.js) |
| Open Source | ✅ Fully | ❌ Proprietary |
| Pricing at Scale | $25/mo Pro flat (then usage) | Blaze pay-as-you-go (can balloon) |
| Vendor Lock-in | Low (plain Postgres) | High (Firestore is unique) |
When to choose Supabase: You want SQL, relational data, open-source portability, and predictable pricing. Real-world data shows that at 10K DAU, Supabase runs $50–100/mo on Pro while Firebase can cost $500–1500/mo on Blaze due to document read/write costs.
When to choose Firebase: You need native mobile SDKs with offline-first support, you’re already deep in the Google Cloud ecosystem, or you genuinely don’t need relational joins.
Supabase vs. Neon
Neon is a serverless Postgres platform — it does one thing (PostgreSQL) and does it exceptionally well, with branching and scale-to-zero.
| Feature | Supabase (Free) | Neon (Launch Plan) |
|---|---|---|
| PostgreSQL Storage | 500 MB | 500 MB |
| Compute | Shared, always-on (pauses after 7d) | Scale-to-zero (suspends after 5 min) |
| Branching | ❌ | ✅ Full copy-on-write branching |
| Auth | ✅ Built-in | ❌ Bring your own |
| File Storage | ✅ 1 GB | ❌ Not included |
| Realtime | ✅ WebSocket subscriptions | ❌ Not included |
| Edge Functions | ✅ Deno-based | ❌ Not included |
| Auto-pause | After 7 days | After 5 minutes |
| Max Projects | 2 | 1 |
When to choose Supabase: You want an all-in-one backend — auth, storage, realtime, and functions bundled with your database. For shipping an actual app, Supabase replaces four separate services.
When to choose Neon: You only need Postgres and you want database branching for CI/CD and dev workflows. If you already have auth (Clerk, Auth0) and use other providers for storage/functions, Neon’s pure Postgres with scale-to-zero is a beautiful fit.
Supabase vs. Appwrite
Appwrite is another open-source Firebase alternative that takes a different architectural approach. Where Supabase wraps PostgreSQL, Appwrite uses its own custom database engine.
| Feature | Supabase (Free) | Appwrite (Free) |
|---|---|---|
| Database Engine | PostgreSQL (relational, SQL) | Custom NoSQL document store |
| Database Storage | 500 MB | 5 GB |
| Auth MAUs | 50,000 | 50,000 |
| File Storage | 1 GB | 2 GB |
| Realtime | ✅ Postgres replication + WebSockets | ✅ SDK-based realtime |
| Edge Functions | ✅ Deno-based | ✅ Node.js-based (runtimes) |
| Self-Hosted | ✅ Fully open-source | ✅ Fully open-source |
| GitHub Stars | 60K+ | 45K+ |
| Vendor Lock-in | Low (plain Postgres) | Medium (proprietary DB engine) |
| Max Projects | 2 | 1 |
When to choose Supabase: You want SQL, relational data, real Postgres extensions (PostGIS, pgvector for AI), and the ability to migrate your database to any other Postgres host if needed. Supabase’s Postgres foundation also makes it far easier to hire for — SQL skills are universal.
When to choose Appwrite: You prefer a document-based data model, need more file storage on the free tier (2 GB vs 1 GB), or want built-in serverless functions without Deno. Appwrite also excels at mobile-first projects with its robust client SDKs.
Supabase vs. AWS RDS Free Tier
| Feature | Supabase (Free) | AWS RDS Free Tier |
|---|---|---|
| Duration | Unlimited | 12 months |
| Database | Postgres 15/16 | db.t3.micro (any engine) |
| Storage | 500 MB | 20 GB (gp2) |
| Auth / Storage / Realtime | ✅ Built-in | ❌ None |
| Management | Supabase handles everything | You manage OS patches, backups, scaling |
| Auto-pause | 7 days | Never |
When to choose Supabase: You want to spend time building features, not managing database servers. For most apps, Supabase’s free tier is more useful than RDS because it includes auth, storage, and APIs on top of Postgres.
When to choose RDS: You need more than 500 MB of database storage, you want full control over Postgres configuration, or you’re already in AWS and committed to their ecosystem.
Tips to Maximize the Supabase Free Tier
1. Keep Your Database Lean — Regularly Audit Tables
The 500 MB database limit fills faster than you think once you add file metadata, logs, or audit trails.
-- Find the largest tables in your database
SELECT
relname AS table_name,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
- Archive or delete old data monthly.
- Use
VACUUMperiodically to reclaim dead rows. - Avoid storing large blobs in the database — use Storage instead and store only the URL.
2. Use Storage Buckets Wisely (1 GB)
Supabase Storage uses S3-compatible object storage behind the scenes.
- Set public/private access correctly from the start — you can’t easily change bucket visibility after users upload files.
- Enable CDN caching for public assets (images, CSS, etc.) — cached egress is free up to 5 GB/month, uncached egress is also 5 GB.
- Use image optimization (compress before upload) — a 1 MB photo compresses to ~200 KB with no perceptible quality loss.
- Schedule a cleanup script for old or unused files.
3. Enable Row-Level Security on Every Public Table
This is the #1 mistake developers make on the free tier. Without RLS, any client with your anon key can read or write every row in every table. Always:
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
Then define at minimum a FOR SELECT policy. Supabase exposes your database directly through PostgREST — RLS is your only defense.
4. Handle the 7-Day Auto-Pause
- If your project is a side project you visit occasionally, set a cron job (GitHub Actions, cron-job.org, or UptimeRobot) to hit your Supabase API once a week:
# .github/workflows/keep-alive.yml
on:
schedule:
- cron: '0 0 * * 0' # Every Sunday
jobs:
ping:
runs-on: ubuntu-latest
steps:
- run: curl -s "${{ secrets.SUPABASE_URL }}/rest/v1/todos?select=count&limit=1" \
-H "apikey: ${{ secrets.SUPABASE_ANON_KEY }}"
- If your project does go to sleep, unpausing takes ~30 seconds from the dashboard. No data is lost.
5. Use the SQL Editor — Not a GUI Client — for Schema Changes
The Supabase SQL Editor is fast, has syntax highlighting, and shows query results inline. For the free tier, avoid connecting heavy GUI tools like DBeaver or pgAdmin — they keep connections open and count toward database limits.
6. Monitor Usage from the Dashboard
Go to Project Settings → Usage in the Supabase dashboard to track:
- Database size growth
- Auth MAU count
- Storage usage
- Egress bandwidth
- Edge Function invocations
Set calendar reminders to check monthly. The biggest surprise is usually storage growth from file uploads or unoptimized images.
7. Generate TypeScript Types and Lint Your Queries
Supabase’s type generator catches mismatches between your schema and your frontend code at build time instead of runtime:
npx supabase gen types typescript --project-id your-project-id > database.types.ts
This alone saves hours of debugging “column doesn’t exist” errors in production.
8. Batch Edge Function Deployments
Each Edge Function invocation (including warm starts) counts toward your 500K/month limit. If you have multiple functions, consider combining related logic into a single function with route-based dispatching:
serve(async (req) => {
const url = new URL(req.url)
switch (url.pathname) {
case '/stripe-webhook': return handleStripe(req)
case '/send-email': return handleEmail(req)
case '/process-image': return handleImage(req)
default: return new Response('Not Found', { status: 404 })
}
})
This reduces invocation counts and keeps your function count manageable.
Summary
Supabase’s free tier is one of the most generous and capable in the BaaS space. With 500 MB of PostgreSQL, 50K MAUs, 1 GB storage, real-time subscriptions, 200 concurrent WebSocket connections, and 500K edge function invocations, you can build and ship real applications — chat apps, mobile backends, SaaS platforms, and content management systems — without spending a cent on infrastructure.
The key tradeoffs are: no automated backups, no branching, shared compute, and the 7-day auto-pause for idle projects. For most MVPs, side projects, and even early-stage startups, these tradeoffs are acceptable. When you outgrow them, the Pro plan at $25/month unlocks point-in-time recovery, 500 concurrent realtime connections, and higher limits across the board.
The bottom line: If you’re building a new application in 2026 and you want a backend that Just Works™ — with real PostgreSQL, not a NoSQL document store — start with Supabase’s free tier. It will carry you from prototype past your first thousand users.
