Your team says the database is backed up nightly. How would you establish whether you could really restore it?
A backup job that exits zero is a belief, not a control. Establishing recoverability means agreeing an RPO and RTO, then rehearsing a restore onto clean infrastructure, measuring how long it took and verifying the result — including everything the dump does not contain.
What the interviewer is scoring
- Does the candidate turn "nightly" into an explicit RPO and RTO agreed with the business
- Whether replication and volume snapshots are distinguished from backups rather than counted as them
- That verification is described as a measured restore rather than as a job that reported success
- Whether the answer covers what lives outside the dump - roles, extensions, encryption keys, configuration
- Does the candidate consider who is able to delete or overwrite the backups
Answer
Turn "nightly" into two numbers
"Backed up nightly" is a description of a cron entry, not of a guarantee, and the first thing to do is convert it into the two numbers the business is actually buying. Recovery point objective is how much data you are willing to lose; a nightly dump and nothing else sets that at up to twenty-four hours, whatever anyone believes. Recovery time objective is how long you may be down while restoring, and nobody knows it until somebody has timed a restore of the current data volume.
Ask those two questions and the answer often collapses the whole discussion. If the tolerable loss is minutes, a nightly job cannot deliver it at any level of care, and the conversation becomes one about continuous archiving instead of one about backup hygiene. In PostgreSQL that means a base backup plus archived write-ahead log, which lets you recover to a chosen point in time rather than to last midnight. In MySQL the equivalent pairing is a physical or logical backup plus the binary logs from that point forward. Either way the periodic copy is the floor and the log stream is what closes the gap.
Replicas and snapshots are not backups
The most common substitution is a replica. A streaming replica protects you from losing a machine, and it protects you from nothing else, because it faithfully reproduces the destructive statement. DROP TABLE replicates. A bad migration replicates. An application bug that overwrites a column for every row replicates, and it does so in under a second.
A delayed replica is a genuine partial answer here, and worth naming as a distinct control: PostgreSQL's recovery_min_apply_delay holds a standby a fixed interval behind the primary, so for the length of that window you have a live copy of the recent past and recovery is a promotion rather than a restore. Its limitation is exactly its window. Notice the damage after the delay has elapsed and the replica has already applied it.
Volume snapshots are the second substitution, and their correctness depends on atomicity. A snapshot of a single volume holding both the data directory and the log is equivalent to pulling the power cable: the engine treats it as a crash on startup and replays its log to a consistent state. The same snapshot taken across two volumes with no coordination between them is torn, and it will restore into something that appears to work and is subtly wrong. If snapshots are part of the plan, the thing to establish is whether the snapshot is atomic across everything the database writes to.
What a restore has to contain that a dump does not
The failure that surprises people during a real recovery is that the data restores and the system still does not run, because a logical dump is a dump of one database and not of the server it lived in. Roles and passwords, tablespace definitions, and cluster-wide settings are separate concerns; in PostgreSQL, pg_dump covers a single database while pg_dumpall --globals-only is what carries the roles. If extensions are in use, the target machine needs the extension binaries installed at compatible versions before the schema will load at all.
Then there is everything outside the database. Configuration that was hand-edited on the old host, the encryption key or KMS grant without which the backup file is noise, the object-storage credentials the restore path itself needs, and the DNS or connection-string change that points the application at the new instance. A restore plan that assumes the surviving infrastructure is the same infrastructure has not been tested against the scenario people most fear.
Five mechanisms, none of them a restore
In January 2017, a GitLab.com engineer working on replication ran a directory deletion against the wrong host and removed a live database directory, after which five separate backup or replication mechanisms turned out not to produce a usable restore. That is the case to keep in mind for this whole topic: an untested backup is a belief rather than a control, and the number of mechanisms you own says nothing about whether any one of them works.
The mechanical ways a backup silently produces nothing are worth being able to list, because each one passes every check that looks at the job rather than at the artefact. The exit status of the dump is discarded by a shell pipeline, so a truncated file is written and the job reports success. The schedule runs on a host that was replaced during a migration and never came back. Retention deletes the oldest copy on a fixed rule, so a corruption discovered three weeks late has already had its last clean copy expired. The archive destination silently stopped accepting writes, which in PostgreSQL has a second consequence people miss: when the archive command keeps failing, the server retains the log segments it cannot archive, and the disk fills on the primary you were trying to protect.
Verification is a restore you timed
The only evidence that counts is a restore. Automate it: take the most recent backup, restore it onto infrastructure built from scratch rather than onto the machine that already has the data, and then check the result rather than the exit code. Check that the object counts and the row counts of the largest tables match what the source reported, that the newest row is as recent as your RPO promises, and ideally that the application starts against it and serves a read. pg_verifybackup checking a base backup against the manifest written alongside it is a useful cheap gate, but it proves the files are intact, not that the cluster starts and the data is complete.
Record how long the whole thing took, because that number is your real RTO and it grows with the data. A logical restore in particular rebuilds every index, so its cost rises faster than the size of the dump suggests, and a plan that was comfortable at fifty gigabytes can be a multi-day outage at two terabytes. The metric worth alerting on is the age of the last verified restore, not the age of the last backup, because that is the one that fails loudly when the pipeline quietly stops working.
Finally, ask who can destroy the copies. If the credentials that run the application, or the account that owns the primary, can also delete the backup bucket, then one compromise or one confident script removes both sides at once. Separate the identity that writes backups from the one that can delete them, and prefer a retention lock the operator cannot lift on impulse.
Nobody has a backup, they have a restore they have or have not performed. Measure the age of the last verified restore and the time it took, and treat those two numbers as the actual guarantee.
Likely follow-ups
- The primary is corrupt and you have WAL archives. Walk me through the recovery, and what you do before you touch anything.
- What does a delayed replica protect you from that a nightly dump does not, and what does it miss?
- WAL archiving has been failing for six hours and nobody noticed. What is happening on the primary right now?
- Someone dropped one table an hour ago. How do you get it back without rolling the whole database backwards?
Related questions
- A maintenance script deletes rows it should not have touched, and nobody notices for six hours. Walk me through the recovery.hardAlso on point-in-time-recovery6 min
- A user submits the form twice and you get two identical orders. Why did the check-then-insert not prevent it, and how do you fix it in the database?hardAlso on postgresql5 min
- Every table in this schema soft-deletes with a deleted_at column. A year in, what has that cost you?mediumAlso on postgresql5 min
- Your routing optimiser produces a good plan and the transport planner changes it every morning. What is going wrong?hardAlso on operations4 min
- You need to migrate the schema of a 400-million-row table on a live system. Talk me through the plan, the backup you would want first, and how the connection pool affects it.hardAlso on postgresql6 min
- This table has fourteen indexes and writes have got slower. How do you work out which ones to drop?hardAlso on postgresql6 min
- Three queries hit the same table with different filters. What composite indexes do you build, and how do you order the columns?hardAlso on postgresql5 min
- You are getting a handful of deadlock errors an hour under load. How do you find the cause, and how do you stop them?hardAlso on postgresql7 min