From NoSQL lock-in to a real Postgres you can query.
Why switch
- ▸Firebase billing is opaque and scales steeply once you leave the free tier.
- ▸Supabase is Postgres: SQL, joins, row-level security, and an exit path that is a pg_dump.
- ▸Auth, storage, realtime and edge functions cover the Firebase feature set on open standards.
The migration
- 01
Model your Firestore data as SQL
The hardest and most valuable step. Firestore collections become tables; nested arrays become JSONB or join tables. Design the schema before touching data.
- 02
Stand up Supabase
Start with Supabase Cloud or self-host the stack. Create the schema, then enable Row Level Security on every table — RLS replaces Firestore security rules.
- 03
Migrate auth
Export Firebase users (CLI: firebase auth:export users.json --format json), then import into Supabase via the admin API, preserving UUIDs where possible. Update password hashes if you can export them; otherwise schedule a reset flow.
- 04
Move the data
Firestore export → transform to CSV/JSON → Supabase import (CSV per table or the migration script pattern). Verify counts per collection against per-table counts.
- 05
Ship a dual-write window
Run both backends for one release cycle: new writes go to both, reads come from Supabase. Flip reads, then stop Firebase writes and export a final archive.
Gotchas — read before you cancel
- ⚠Firestore listeners become Supabase Realtime subscriptions — different semantics, test reconnection.
- ⚠Cloud Functions are not edge functions 1:1 — port triggers as database webhooks or Supabase Edge Functions.
- ⚠Storage security rules need an RLS-equivalent policy review on buckets.
Only cancel when this is true
- ✓Row counts match Firestore collection counts exactly
- ✓A user can sign in with their old credentials (or through the planned reset)
- ✓RLS blocks a cross-tenant read attempt in testing
Replacing more than one tool?
The Stack Composer bundles Supabase with the rest of your open-source stack into one docker-compose file.
Open in Stack Composer →