The call always sounds the same. Someone ran an update against the production database without a WHERE clause, or a deploy script dropped a table it was supposed to rename, and a few thousand rows are now wrong. The database runs on Cloud SQL, the backup toggle is on, and the room stays calm for about four minutes. Then somebody asks the real question: has anyone here ever restored one of those backups?
Most of the time the answer is no. Backups were switched on when the instance was created, nobody has touched the setting since, and the retention window is whatever the default was. That is not a recovery plan. It is a hope with a checkbox next to it. What follows is the checklist we run before we are willing to call a client database safe.
Step 1: find out what "backups are on" actually means here
Two settings get confused constantly, and they do different jobs. Automated backups give you a set of restore points, one per day. Point-in-time recovery keeps the transaction logs between those points, so you can land on a specific moment instead of last night. You need both to recover from a mistake made at 11:40 this morning, and Google's documentation is explicit that point-in-time recovery requires automated backups and transaction logging to be enabled on the source instance.
How the instance was created decides what you got for free. Automated backups are enabled by default when you create an instance in the Google Cloud console, but if the instance came from the gcloud CLI, Terraform or the Cloud SQL Admin API, you have to enable them yourself (checked 20 October 2026). Point-in-time recovery is on by default on Cloud SQL Enterprise Plus instances whatever the creation method, and has to be switched off deliberately. On Enterprise edition it is a decision somebody had to make. Plenty of Terraform-provisioned databases we inherit have neither.
Then we read three values rather than trusting the toggle:
- Backup retention. Automated backup retention runs from 7 to 365 days, and the default depends on edition: 7 days on Cloud SQL Enterprise, 15 days on Enterprise Plus. Per-engine pages do not fully agree on the lower bound, so check the page for your engine rather than ours.
- Transaction log retention. This is the one people have never seen. On Enterprise Plus the valid range is 1 to 35 days with a default of 14. On Enterprise it is 1 to 7 days with a default of 7. That number, not the backup count, is how far back a point-in-time recovery can reach.
- Backup window and location. You set a start time in 24-hour UTC, and that time opens a four-hour window inside which the backup can begin. Backups go to the multi-region geographically closest to the instance unless you set a custom location, and Google's guidance is to leave the default unless a regulation forces a specific region. If somebody changes the location later, the backups already taken stay where they were.
Say a 12-person company with one ordering database. The honest read is usually seven daily restore points and seven days of logs, both inherited by accident, and nobody has checked whether that window covers the kind of mistake the team actually makes.
Step 2: pick the recovery option before you need it
"Backed up" is not one thing. Each option covers a different failure, and the cheapest mistake in this whole area is assuming the one you have covers the one you get. We put the options in front of the client and pick explicitly.
| Option | What it recovers from | What it does not cover |
|---|---|---|
| Automated backup (standard) | A bad change you notice within the retention window, back to a daily restore point | Anything between restore points; deletion of the whole project |
| Point-in-time recovery | A specific moment inside the transaction log retention period, such as just before a bad script ran | Anything older than the log retention days; restoring in place, since PITR always creates a new instance |
| Enhanced backup in a backup vault | Deletion or tampering, including by someone with project access, with retention up to 10 years on the MySQL and PostgreSQL pages | Recent data loss at fine granularity; it is a vault, not a replacement for PITR |
| High availability | A zone failure, with automatic failover inside the region | Logical mistakes: a bad UPDATE replicates to the standby instantly |
| Cross-region read replica | A region-wide outage | Logical mistakes, for the same reason |
| Export to Cloud Storage | Needing a copy outside Cloud SQL, for an audit or a move to another platform | Fast recovery; an export is not a restore point and is not scheduled for you |
Read the right-hand column twice. High availability and replicas are the two options small teams mistake for their backup, and neither protects against what breaks databases most often: a person running correct SQL against the wrong rows.
Step 3: run the restore drill
This is the step that turns a setting into a guarantee, and it is the step nobody does. We schedule an hour, restore to a new instance, and check the data. Restoring to a new instance matters: restoring over an existing instance overwrites all of its current data, including the previous point-in-time recovery logs, which means a panicked restore to the wrong target destroys the fine-grained recovery you still had.
The drill, in order:
- List what you actually have. Not the toggle, the backups. Confirm the newest successful backup is from last night and not from the last time someone changed an instance flag.
- Restore the newest backup to a brand-new instance. With gcloud this is
gcloud sql backups restore, and when the backup came from a different instance you have to say so with--backup-instance. - Do a point-in-time restore to a timestamp. Pick a moment from yesterday afternoon. This proves the transaction logs are really there, which is a separate fact from the backups being there.
- Query the restored copy like an application would. Row counts on the three tables that matter, the newest order, the newest user. A restore that starts and a restore that contains your data are different events.
- Write down the clock time. From "go" to "application could point at this", in minutes. That number is your real recovery time, and it is the only version of it worth quoting to a client.
- Delete the test instance. Restored instances bill like any other instance, and an abandoned one is a recurring charge nobody remembers approving.
gcloud sql backups restore BACKUP_ID \
--restore-instance=restore-drill-20261020 \
--backup-instance=prod-db
gcloud sql instances patch prod-db \
--enable-bin-log \
--retained-transaction-log-days=7
The flag that enables transaction logging is named per engine, so read your engine's page before copying that second command. The drill also surfaces the constraints that only bite during a real incident: a backup restores only into an instance running the same database version it was taken on; the target cannot be a read replica, and an existing target with replicas needs them deleted first; instance flags are not carried over; and restores are rate limited by a small token bucket that refills roughly every ten minutes, which is a surprise when you are retrying under pressure.
We also check who can run the restore, because it is cheap to check. A cross-project restore needs cloudsql.instances.restoreBackup on the target project and cloudsql.backupRuns.get on the source, and a retained or final backup also needs cloudsql.instances.create. If the only person holding those permissions is on a plane, the backup is theoretical.
Step 4: decide when the database is allowed to restart
Managed means Google patches the instance, and patching means a restart. For a database behind a storefront, the difference between a good maintenance policy and no policy is whether that restart lands at 3 a.m. on a Tuesday or during the Friday invoice run.
Two controls do this. The maintenance window sets the day and hour you prefer. A deny maintenance period is a block of days, up to 90, when Cloud SQL will not schedule maintenance at all, and it takes precedence over the window when the two conflict. Setting a deny period on a primary instance also denies maintenance on its replicas, which is the behaviour you want and not the one people expect.
Three details we raise with clients. A deny period cannot be set on an instance whose maintenance version is more than 12 months old, so the shop that wants December protected has to accept an update in November to get it. Omitting the year makes the period recur annually, usually the right answer for a seasonal peak. And the engine pages disagree on whether a deny period survives a stop and start, so on anything load-bearing we verify that on the engine in front of us rather than quoting a rule.
gcloud sql instances patch prod-db \
--deny-maintenance-period-start-date=11-15 \
--deny-maintenance-period-end-date=01-15 \
--deny-maintenance-period-time=00:00:00
Self-service maintenance is not blocked by a deny period, so the team can still apply an update deliberately when it suits them.
Step 5: price the protection honestly
Every option above costs something, and small teams get sold the expensive one first. Google's pricing documentation states that a high availability configuration costs twice a standalone instance, covering CPU, RAM and storage (checked 20 October 2026). That is the right spend for a database whose downtime stops revenue, and the wrong first purchase for an internal reporting database nobody touches on a Sunday. We have seen the second case paying for the first.
The cheaper moves come first. Longer transaction log retention buys more recoverable history, and the documentation notes that logs held on the instance need more storage while logs held in Cloud Storage add no per-instance charge up to the maximum point-in-time recovery length. A vault buys protection against deletion from inside the project, with the trade-off that backups under enforced retention cannot be removed early and keep billing for the whole period. Backup storage is billed separately from instance storage, so check the current rate on Google's pricing page rather than on anyone's blog, including this one.
The order we recommend for a small business: get both settings on and retention set deliberately, run the drill, set the maintenance window and a deny period for your peak season, then decide about high availability with a number in front of you. Spending in that order is how the protection ends up matching the risk.
How Guanacos Tech helps
We do this as a fixed piece of work: read the current backup and point-in-time recovery settings, run a real restore to a scratch instance and time it, set the maintenance window and the deny period around your business calendar, and leave you a one-page recovery note that says what to run, who can run it, and how long it takes. Where a database is one part of a larger setup, it gets handled alongside the rest of the project in our Google Cloud consulting for small teams work. Book a 30-minute call if you want to know whether your database would come back, and we will tell you what we find.
Sources
- Google Cloud: Cloud SQL backups overview
- Google Cloud: configure point-in-time recovery (PITR)
- Google Cloud: restore an instance using a backup
- Google Cloud: choose your backup option
- Google Cloud: maintenance updates on Cloud SQL instances
- Google Cloud: about high availability in Cloud SQL
- Google Cloud: Cloud SQL pricing