| Export + import script | Cloud Functions triggers | Batch ETL tool | Fireloom | |
|---|---|---|---|---|
| Freshness | Whenever you re-run it | Seconds | Each scheduled run | Seconds |
| Deletes | Only if you rebuild the table | If you handle them | Often needs a full refresh | Streamed, and checked daily |
| Subcollections at any depth | Yes (exports include them) | One trigger per path pattern | Varies by tool | Yes, one table, parent links |
| Existing data (backfill) | Yes | Separate script | Yes | Yes, from an export or directly |
| Firestore reads after the first copy | Every document, every export | None for the trigger itself | Often every document, every run | Only changed documents |
| Restarts and outages | n/a | Retries you configure; events can arrive out of order | Next run | Resumes from checkpoints |
| Code to write and run | Converter and loader | Functions, schema, deploys | Config; often a self-hosted tool | None |
| Access to your Firestore | Yours | Runs in your project | Usually a read key | Read-only key, plus export-only role |
Export and import
gcloud firestore export writes LevelDB files to Cloud Storage. To use them in Postgres you need a converter and a loader. It's a reasonable choice for a one-time copy, such as a migration rehearsal. As a sync it's expensive: every export reads every document, and the result is stale the moment it finishes.
Cloud Functions triggers
An onDocumentWritten trigger can write each change to Postgres within seconds. You own the code: the schema, connection pooling from functions, a backfill for existing data, delete handling, retries, and ordering (events can be delivered more than once and out of order, so writes need versions). Nested subcollections need their own trigger paths. It works well for teams that want full control and have time to maintain it.
Batch ETL tools
General-purpose ELT tools move data on a schedule. Firestore has no change cursor, so many connectors re-read whole collections on each run, which costs one read per document every time. Check how your tool handles deletes and subcollections before relying on it.
Fireloom
Fireloom listens to Firestore's change stream and keeps one Postgres table current within seconds, deletes and subcollections included. The first load reads each document once (or uses an export), restarts resume from checkpoints, and a daily count check catches drift. You connect two databases; there's nothing to deploy. Inside the sync engine covers the details.
When it's not the right fit: if you need Postgres-to-Firestore writes, typed columns per field today, or a destination other than Postgres, Fireloom doesn't do that yet.
Try it free
Up to 100,000 documents per Firebase project, real-time streaming included.
Start syncing free