Firestore to Postgres, with every password still working.
Bring the app to a Postgres backend you can query, branch, and export. Scribase imports Firestore, Firebase Auth including scrypt password hashes, and Cloud Storage, then shows a table-by-table verification report.
Four steps in the console: connect the source, review a dry run that writes nothing, run it, and read the verification report. Your place is saved if you close the tab; credentials never are.
Everything a Firebase app runs on
- Firestore
- Each collection group becomes a Postgres table with path, id, parent_path, and a jsonb data column. Subcollections become parent__child tables.
- Typed values
- Timestamps, references, geo points, bytes, maps, and arrays convert to plain JSON you can index and query with SQL.
- Firebase Auth
- Every user with email, phone, verification state, custom claims, and linked Google, Apple, GitHub, Facebook, Microsoft, and Twitter identities.
- Passwords
- Firebase modified-scrypt hashes, carried with your project hash parameters and verified natively at sign-in.
- Cloud Storage
- Every bucket and object, copied into private buckets with a SHA-256 check on each file.
What stays the same
- Passwords: users sign in with the password they already have
- Old Firebase UIDs, mapped to new ids in public.firebase_auth_uid_map so app data that stored UIDs still joins
- Document ids and paths, so references between documents still resolve
- Disabled users stay disabled
What you do by hand
- Firestore and Storage security rules are not SQL. Every imported table starts with row level security on and no policies, so nothing is exposed until you write policies.
- Cloud Functions are not part of an export. Port them to Scribase functions (Deno).
- Multi-factor enrollments cannot be exported from Firebase; affected users re-enroll.
Plan, run, prove
Step 1
Export Firestore and Storage
The scribase package reads Firestore and Cloud Storage through Google REST APIs with a service account. No Firebase SDK needed.
import { writeFirebaseExport } from 'scribase/importers'; await writeFirebaseExport({ projectId: 'my-app', serviceAccount: JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT!), directory: './firebase-my-app', authExportPath: './auth.json', buckets: ['my-app.appspot.com'], });Step 2
Export Auth with the Firebase CLI
Password hashes are only available through the Firebase CLI. Copy the hash parameters from Authentication, Users, Password hash parameters.
firebase auth:export auth.json --format=JSON --project my-appStep 3
Plan, then run
Upload small exports in the console wizard, or place the folder in the server exports directory. The dry run shows every collection, user, and file first.
await scribase.imports.createFrom({ source: 'firebase', sourceFirebaseProjectId: 'my-app', sourceExportDir: 'firebase-my-app', sourceHashConfig: { base64_signer_key: '…', base64_salt_separator: 'Bw==', rounds: 8, mem_cost: 14 }, destination: { organizationId: 'acme', projectId: 'app', environmentId: 'production' }, verifyLogin: { email: 'you@yourapp.com', password: process.env.TEST_PASSWORD }, });Step 4
Read the report
Row counts and checksums per table, auth user and object counts, and a sign-in proof that recomputes your test account against the migrated scrypt hash.
Old versus new, table by table
Every import ends with this. The verdict is VERIFIED only when every row count and checksum matches, every policy is present, and the sign-in proof passes. The numbers below are an example of the format.
| Table | Firebase rows | Scribase rows | Checksum |
|---|---|---|---|
| public.profiles | 12,408 | 12,408 | match |
| public.orders | 88,214 | 88,214 | match |
| public.order_items | 240,977 | 240,977 | match |
Want us to host it? Join the Firebase waitlist.
The importer runs today on any Scribase you operate. Hosted Scribase opens by source, in waitlist order. Tell us your size and we will reach out when your slot opens.
Migrating from Firebase
How can passwords survive the move?
Firebase hashes passwords with a modified scrypt keyed by your project signer key. Scribase stores each hash together with those parameters and verifies them at sign-in the same way Firebase does, so no user needs a reset.
Will my data still be documents?
Each document lands as a row with its fields in a jsonb column, so you can query it with SQL right away (data->>'email') and move hot fields into real columns later with ordinary migrations.
Do I need to keep paying Google during the move?
Only for the export itself. Reading documents and objects counts toward your normal Firestore and Storage usage for one pass.