+1 (762) 572-1994

Backups, PITR, and the Restore You Have Never Run

Most of the Firebase apps we audit have no tested recovery path. Some have no backups at all. The ones that do have backups have never restored from them, which is close to the same thing.

The usual reason is that nothing ever visibly breaks. Firestore does not lose your data; Google's durability is not the risk. The risk is a migration script with a bad where clause, a Cloud Function that deletes children on a parent write and gets triggered by a backfill, or an admin token in a CI job with more scope than anyone remembers. Those failures are self-inflicted, fast, and complete — and a backup you have never restored is a guess, not a plan.

Here is what the three mechanisms actually do, what they cost, and what they do not cover.

Three mechanisms, not one

Managed backup schedules take a snapshot of a Firestore database on a daily or weekly cadence, with a retention window you choose. Configure once:

gcloud firestore backups schedules create \
  --database='(default)' \
  --recurrence=daily \
  --retention=7d

# weekly, longer horizon, for the "we noticed in March" case
gcloud firestore backups schedules create \
  --database='(default)' \
  --recurrence=weekly \
  --day-of-week=SUN \
  --retention=14w

Run both. Daily catches the bad deploy; weekly catches the slow corruption nobody noticed for a month.

Point-in-time recovery (PITR) keeps older versions of your documents so you can read the database as it was at a specific timestamp inside the retention window — minute granularity for the recent past, rather than a once-a-day snapshot.

gcloud firestore databases update --database='(default)' --enable-pitr

PITR is the tool for "the migration started at 14:05 and we killed it at 14:19." A nightly backup loses you a day; PITR loses you fourteen minutes.

Exports (gcloud firestore export gs://bucket/path) write documents to Cloud Storage in a portable format. That is the one to use when you want the data outside Firestore — loading into BigQuery, seeding a staging database, or keeping a copy in an account that a compromised production credential cannot reach.

The cost difference is not small

Backups and PITR bill as storage. Exports bill as operations: an export is charged one document read per document, an import one document write per document, on top of the Cloud Storage bill. Managed backups do not charge per document.

That gap decides your cadence. Take a 20-million-document database. A nightly export costs 20M reads every night — 600M reads a month, which at published us-central read pricing (about $0.06 per 100,000 document reads at the time of writing; confirm current rates before you budget) is roughly $360/month to hold a copy you hope never to use. The same protection from a daily managed backup schedule is a storage line measured in GiB-months, typically single-digit dollars.

So: managed backups daily, PITR on, and exports on a much slower cadence — monthly, or on demand before a risky migration — for the off-platform copy. Teams that got this backwards are usually paying hundreds of dollars a month for the weakest of the three options.

You cannot restore in place

This is the part that surprises people during an incident. A restore does not overwrite your existing database. It creates a new database in the same project, from a backup or from a PITR timestamp:

# from a managed backup
gcloud firestore databases restore \
  --source-backup=projects/PROJECT/locations/LOC/backups/BACKUP_ID \
  --destination-database=restore-20260913

# from a point in time
gcloud firestore databases restore \
  --source-database='(default)' \
  --snapshot-time=2026-09-13T14:05:00Z \
  --destination-database=restore-1405

So recovery is always two jobs. Job one: get the old data into a new database, which the platform does for you. Job two: decide what to do with it — and that one is yours to write. Usually it is a reconciliation script that reads the restored database, diffs the affected collection against production, and repairs only the documents the bad job touched. Everything written by real users after the incident must survive. A wholesale swap throws away hours of legitimate traffic to undo fourteen minutes of damage.

Write that diff-and-repair script before you need it, even in rough form. During an incident nobody writes careful code.

What a Firestore backup does not contain

A restored database is not a restored application. The gaps we find on audits, in rough order of how badly they hurt:

  • Auth users. Firebase Authentication is a separate system. Your Firestore backup has the users/{uid} profile documents and none of the accounts, passwords, or providers behind them. Export them on a schedule: firebase auth:export users.json --project PROJECT, stored somewhere production cannot write. Password hashes come with their parameters, so a re-import preserves logins.
  • Cloud Storage objects. Uploaded files live in a GCS bucket. Turn on object versioning and a retention policy on that bucket; Firestore backups have nothing to do with it.
  • Security rules and indexes. The new database ID needs its own rules and index deployment. If your firebase.json targets (default) by name, a restored database starts with whatever the default deploy gave it — check before you point any client at it.
  • Realtime Database. Separate product, separate backup story.
  • Scheduled functions and their state. Any job that was mid-run during the incident window will run again against a different dataset.

Rehearse it once a quarter

The drill is short and it is the only part of this that produces confidence:

  1. Restore last night's backup into a scratch database.
  2. Deploy rules and indexes to it.
  3. Point a staging build at it and sign in as a test user.
  4. Run the diff script against production for one collection, in dry-run mode, and read the output.
  5. Delete the scratch database. Write down how long steps 1 through 4 took.

That last number is your real recovery time objective. It is usually two to four hours the first time and under an hour once someone has done it before. Whatever it is, it is a fact you can tell your customers, instead of an assumption.

If you would rather have someone else run the first drill with you, that is a normal piece of a Firebase audit — recovery path included, along with the rules and cost work.