PgBackRest Restore Scenario
Rescuing a Dropped PostgreSQL Database: Two pgBackRest Paths to Success
That sinking feeling when a DROP DATABASE
command slips through... we've all been there, or at least dreaded it. The good news is that with robust tools like PostgreSQL and pgBackRest, recovery is often very achievable. I recently put this to the test after "accidentally" dropping a database named NEWDB
and wanted to share two distinct, successful methods I used to bring it back.
The Setup:
- A PostgreSQL cluster with several databases, including one I created called
NEWDB
. - Data was added to
NEWDB
. - A full backup was taken using pgBackRest while
NEWDB
existed. - Then, the fateful command:
DROP DATABASE "NEWDB";
The mission: Restore NEWDB
without impacting the other live databases.
Method 1: The Classic & Controlled - Restore to Temporary, then pg_dump
/pg_restore
This is a well-trodden and highly reliable path for surgically extracting specific data from a full backup. It offers maximum control and isolates the recovery process until the very end.
The Core Idea:
You restore the entire cluster from a backup (taken before the drop) to a completely separate, temporary location. From this temporary, running instance, you then use pg_dump
to export just the database you need (NEWDB
), and finally, pg_restore
to load it into your live production cluster.
Key Steps to Success:
- Identify the "Golden" Backup: Use
pgbackrest --stanza=mydb info
to find the label of the backup set that was taken beforeNEWDB
was dropped. This backup containsNEWDB
. - Prepare a Temporary Restore Zone: Create new, empty directories for the temporary instance's data directory and its WAL files (especially if your main setup uses a symlinked
pg_wal
). Ensure thepostgres
user has ownership.# Example paths sudo mkdir -p /mnt/pg_temp_restore/data sudo mkdir -p /mnt/pg_temp_restore/wal # If using symlinked WALs sudo chown -R postgres:postgres /mnt/pg_temp_restore sudo chmod -R 700 /mnt/pg_temp_restore
- Perform a Point-In-Time Restore (PITR) to the Temporary Zone:
This is crucial. You want to restore the state of the cluster just beforeNEWDB
was dropped.pgbackrest --stanza=mydb \ --pg1-path=/mnt/pg_temp_restore/data \ --set=[LABEL_OF_BACKUP_WITH_NEWDB] \ --type=time \ --target="[YYYY-MM-DD HH:MM:SS_JUST_BEFORE_NEWDB_DROP]" \ --target-action=promote \ restore
(Replace bracketed placeholders with your actual values. If you use a symlinked
pg_wal
in your main setup, after the restore command completes and before starting the temporary instance, go into/mnt/pg_temp_restore/data
, remove thepg_wal
directory pgBackRest created, and create a symlink namedpg_wal
pointing to/mnt/pg_temp_restore/wal
). - Configure & Start the Temporary PostgreSQL Instance:
Edit thepostgresql.conf
in/mnt/pg_temp_restore/data
to use a port different from your live cluster (e.g.,port = 5433
). Then, start this temporary instance:pg_ctl -D /mnt/pg_temp_restore/data -o "-p 5433" -l /mnt/pg_temp_restore/logfile start
Check its logfile for successful startup and recovery.
NEWDB
should be present here! - Dump
NEWDB
from the Temporary Instance:pg_dump -p 5433 -U postgres -Fc -f /tmp/NEWDB_rescue.dump NEWDB
- Shutdown & Clean Up the Temporary Instance:
pg_ctl -D /mnt/pg_temp_restore/data -m fast stop sudo rm -rf /mnt/pg_temp_restore # Free up the temporary space
- Restore
NEWDB
into Your Live Cluster:
First, connect to your live PostgreSQL cluster and create an empty placeholder for the database:
Then, from your server's command line, use-- In psql connected to your live cluster CREATE DATABASE "NEWDB";
pg_restore
:pg_restore -U postgres -d NEWDB -v /tmp/NEWDB_rescue.dump rm /tmp/NEWDB_rescue.dump # Clean up the dump file
Why this method shines:
- Safety: Your live cluster isn't touched directly until the final, controlled
pg_restore
step. - Predictability: You know exactly what you're getting by isolating the restore.
- Universality: Works regardless of special backup flags or specific PostgreSQL/pgBackRest versions.
Method 2: The In-Place PITR with --delta
- A Surprising Shortcut
Sometimes, a more direct approach can work, especially when the scope of the "damage" (the dropped database) is well understood and you have a precise target time. In my experiments, this method also successfully brought NEWDB
back.
The Core Idea:
Use pgBackRest to perform a Point-In-Time Recovery (PITR) directly onto your existing data directory using the --delta
flag. The --delta
flag tells pgBackRest to update the target directory rather than requiring it to be empty. The magic lies in setting a PITR target time before the database drop was committed to the WALs.
Key Steps to Success:
- Identify a Suitable Backup and Target Time: pgBackRest will choose the latest backup that can satisfy the PITR target. You need to specify a
--target
timestamp that is just beforeNEWDB
was dropped.# Example: If NEWDB was dropped around 2025-05-31 20:35:00 # Target time could be "2025-05-31 20:32:00" as in my successful test
- Perform the Delta PITR Restore:
(Ensure your live PostgreSQL cluster is stopped before running this if it directly modifies the primary PGDATA).pgbackrest --stanza=mydb \ --log-level-console=info \ --type=time \ --target="[YYYY-MM-DD HH:MM:SS_JUST_BEFORE_NEWDB_DROP]" \ restore --delta
- pgBackRest will compare your live
/pgdata/mydb/data
(whereNEWDB
's files are now missing) with the chosen backup (whereNEWDB
's files exist). - It will copy the "missing" files for
NEWDB
back into your live data directory. - It will also set up
postgresql.auto.conf
for PITR to your specified--target
time.
- pgBackRest will compare your live
- Handle Symlinked
pg_wal
(If Applicable):
The restore might replace yourpg_wal
symlink with a real directory. Before starting PostgreSQL,cd
into your data directory,sudo rm -rf pg_wal
, and recreate your symlink:sudo ln -s /your/custom/wal_location/pg_wal pg_wal
. - Start PostgreSQL & Promote:
Start your PostgreSQL server. It will enter recovery mode.
Connect viasudo systemctl start postgresql-17 # Or your service name
psql
and monitor recovery:SELECT pg_is_in_recovery(); -- Once logs indicate it has reached the target or is ready: SELECT pg_promote();
- Verify:
NEWDB
should now be back in your\l
list!
Why this method can work (and did for me):
- The
--delta
restore put the physical files forNEWDB
back. - The precise
--target
time for PITR ensured that the WAL replay stopped before replaying theDROP DATABASE "NEWDB";
command.
Important Note on the Delta Method:
While this worked in my controlled experiment, it's a more "expert" maneuver. It directly modifies your live data directory. Success hinges on a very accurate target time and a clear understanding of the state of your live data versus the backup. For critical production data, the isolation of Method 1 often provides greater peace of mind.
Conclusion:
pgBackRest, combined with PostgreSQL's robust Point-In-Time Recovery, offers powerful ways to recover from data loss. Whether you choose the meticulous control of a temporary restore and pg_dump
, or a carefully targeted in-place delta PITR, the key is understanding your tools, your backups, and the timeline of events.
And remember, no matter how good your recovery tools are, nothing beats regularly testing your restore procedures!
Comments
Post a Comment