If you've ever lost hours of database changes because your last pg_dump was too old, you already understand why pgBackRest for PostgreSQL backups and recovery exists. pgBackRest is a purpose-built backup and restore tool for PostgreSQL that handles full backups, incremental backups, WAL archiving, and point-in-time recovery, all in one package.
Unlike pg_dump, which gives you a snapshot of a single moment, pgBackRest continuously archives your Write-Ahead Log (WAL) so you can recover to any specific second. This guide teaches the entire setup.
What is pgBackRest?
pgBackRest is an open-source backup solution built specifically for PostgreSQL. At the current time, the latest stable release is pgBackRest 2.58.0, released in January 2026. It supports local repositories, remote servers, S3-compatible storage, Azure Blob, and GCS. Key features include:
- Full, differential, and incremental backup types.
- Continuous WAL archiving for point-in-time recovery.
- Parallel compression using zstd, lz4, or gzip.
- AES-256-CBC encryption for backup repositories.
- Configurable retention policies for backups and WAL archives.
- A check command to verify the entire pipeline is working before you need it.
Running PostgreSQL on a reliable dedicated server is important for a stable backup environment. A PerLod Dedicated Server gives you full control over disk layout, I/O throughput, and network configuration, which directly affects backup and restore speed.
How PITR (Point-in-Time Recovery) Works
Point-in-Time Recovery combines two things:
- A base backup: a full copy of all database files at a point in time.
- Continuous WAL archiving: every change is recorded in WAL segments, stored as they are completed.
When a disaster happens, PostgreSQL restores the base backup and then replays WAL segments one by one, stopping exactly at your chosen timestamp. This means you can recover to any second between the last base backup and the last archived WAL segment, not just to your last pg_dump snapshot.
Important Note: Recovery in PostgreSQL is greedy by default. Without specifying a target time or restore point, PostgreSQL will replay every available WAL segment and bring the database to the most recent state, including any accidental deletes. Always set --target when doing PITR.
Prerequisites to Set up pgBackRest for PostgreSQL Backups and Recovery
Before you begin to set up pgBackRest for PostgreSQL backups and recovery, make sure you have:
- Ubuntu 22.04 or higher. You can also use Debian 11 or higher.
- PostgreSQL 14, 15, or 16 is already installed and running.
- A dedicated partition or directory for the backup repository, separate from the data directory.
- sudo access on the server.
Once you are done, you can proceed to the following steps to set up pgBackRest for PostgreSQL backups and recovery.
Step 1: Install pgBackRest
On Ubuntu and Debian, pgBackRest is available directly from the apt repositories. Run the system update and install pgBackRest:
sudo apt updatesudo apt install pgbackrest -y
Step 2: Create Required Directories
pgBackRest needs a repository directory to store backups and WAL archives and a log directory. For this purpose, run the commands below:
sudo mkdir -p /var/lib/pgbackrestsudo chmod 750 /var/lib/pgbackrestsudo chown postgres:postgres /var/lib/pgbackrest sudo mkdir -p /var/log/pgbackrestsudo chmod 770 /var/log/pgbackrestsudo chown postgres:postgres /var/log/pgbackrest sudo mkdir -p /etc/pgbackrest/conf.dsudo touch /etc/pgbackrest/pgbackrest.confsudo chmod 640 /etc/pgbackrest/pgbackrest.confsudo chown postgres:postgres /etc/pgbackrest/pgbackrest.conf
Step 3: Configure pgBackRest
Now you must edit the pgBackRest config file. Open the config file:
sudo nano /etc/pgbackrest/pgbackrest.conf
Add the following configuration. Replace main with your stanza name, which is a logical name for your PostgreSQL cluster, and adjust pg1-path to match your actual data directory:
[main]pg1-path=/var/lib/postgresql/16/main [global]repo1-path=/var/lib/pgbackrestrepo1-retention-full=2repo1-retention-diff=7repo1-cipher-type=aes-256-cbcrepo1-cipher-pass=YourStrongPassphraseHere process-max=4compress-type=zstcompress-level=6 log-level-console=infolog-level-file=detaillog-path=/var/log/pgbackrest
What each option does:
| Option |
Purpose |
| pg1-path |
Path to PostgreSQL data directory |
| repo1-path |
Where backups and WAL archives are stored |
| repo1-retention-full=2 |
Keep the last 2 full backups |
| repo1-retention-diff=7 |
Keep the last 7 differential backups |
| repo1-cipher-type=aes-256-cbc |
Encrypt repository with AES-256 |
| process-max=4 |
Parallel processes for faster backup/restore |
| compress-type=zst |
Use zstd compression (faster than gzip) |
Step 4: Configure PostgreSQL for WAL Archiving
At this point, you must edit your PostgreSQL configuration file. For example, on Ubuntu 22.04 with PostgreSQL 16, this is at /etc/postgresql/16/main/postgresql.conf:
sudo nano /etc/postgresql/16/main/postgresql.conf
Add or modify these lines:
wal_level = replicaarchive_mode = onarchive_command = 'pgbackrest --stanza=main archive-push %p'archive_timeout = 60max_wal_senders = 3
Restart PostgreSQL to apply the changes:
sudo systemctl restart postgresql
Step 5: Create the Stanza
A stanza is just the pgBackRest term for a named configuration group connected to one PostgreSQL cluster. You can create it with:
sudo -u postgres pgbackrest --stanza=main stanza-create
In your output, you should see:
stanza-create command end: completed successfully
Step 6: Verify the WAL Archiving Configuration
You can easily run the check command to verify that WAL archiving is working correctly:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info check
A successful output looks like this:
1P00 INFO: check repo1 configuration (primary)2P00 INFO: check repo1 archive for WAL (primary)3P00 INFO: WAL segment 000000010000000000000001 successfully archived4P00 INFO: check command end: completed successfully
If you see errors here, do not proceed. Fix the issue first.
You can also verify your WAL archiving status from inside PostgreSQL:
1SELECT name, setting2FROM pg_settings3WHERE name IN ('archive_mode', 'archive_command', 'archive_timeout', 'wal_level');
And check for archiving failures:
1SELECT2 archived_count,3 failed_count,4 last_archived_wal,5 last_archived_time,6 now() - last_archived_time AS archive_lag7FROM pg_stat_archiver;
A non-zero failed_count means WAL segments are piling up on disk. Troubleshoot immediately.
Step 7: Take Your First Full Backup with pgBackRest
Use the command below to run your first full backup:
sudo -u postgres pgbackrest --stanza=main --type=full --log-level-console=info backup
Output will show something like:
1P00 INFO: backup command begin 2.58.02P00 INFO: execute backup start3P00 INFO: full backup size = 25MB, file total = 9634P00 INFO: backup command end: completed successfully
Check what's in the repository:
sudo -u postgres pgbackrest info
This shows you the backup catalog, including label, start and stop timestamps, backup size, and WAL range.
Tips: If you're working with PostgreSQL extensions like pgvector for AI workloads, you can check out this guide on installing pgvector on PostgreSQL. Proper backup coverage is equally important for vector-enabled databases.
Step 8: Run pgBackRest Differential and Incremental Backups
Understanding backup types is key to building a solid strategy:
| Type |
What It Backs Up |
When to Use |
| Full |
All database files |
Weekly |
| Differential |
Changes since last full backup |
Daily |
| Incremental |
Changes since the last backup of any type |
Every 6 hours |
Run a differential backup with:
sudo -u postgres pgbackrest --stanza=main --type=diff --log-level-console=info backup
Run an incremental backup with:
sudo -u postgres pgbackrest --stanza=main --type=incr --log-level-console=info backup
Notice the size difference; a differential or incremental backup is much smaller than a full backup because it only stores changed blocks.
Step 9: Set Up a pgBackRest Backup Schedule with Cron
You can easily automate backups with cron. Open the crontab:
sudo crontab -u postgres -e
0 2 * * 0 pgbackrest --stanza=main --type=full backup 0 2 * * 1-6 pgbackrest --stanza=main --type=diff backup 0 */6 * * * pgbackrest --stanza=main --type=incr backup
This gives you weekly full backups, daily differentials, and 6-hourly incrementals.
Step 10: Understanding Retention Policy in pgBackRest
The retention settings you put in pgbackrest.conf control how long backups are kept:
repo1-retention-full=2 repo1-retention-diff=7
When a full backup expires, all differential and incremental backups that depended on it are automatically removed. WAL archives older than the oldest retained full backup are also cleaned up automatically.
To retain backups by time instead of count, you can use:
repo1-retention-full-type=timerepo1-retention-full=30
Warning: Do not expire WAL archives independently of backups. If you remove WAL segments that sit between two retained backups, PITR into that gap becomes impossible.
Step 11: Live PITR Test | Validate Your Recovery Plan
At this point, by running a full recovery (PITR) scenario, you can validate your plan.
First, create test data with:
12sudo -u postgres psql3 45CREATE TABLE important_orders (6 id serial PRIMARY KEY,7 customer text,8 amount numeric9);10 11INSERT INTO important_orders (customer, amount)12VALUES ('Alice', 500.00), ('Bob', 750.00), ('Charlie', 1200.00);13 1415SELECT current_timestamp;16
Then, take an Incremental Backup:
sudo -u postgres pgbackrest --stanza=main --type=incr backup
Then, simulate data loss:
12DROP TABLE important_orders;3 45SELECT * FROM important_orders;6
To recover to the moment before the drop, stop PostgreSQL first:
sudo systemctl stop PostgreSQL
Clear the data directory and restore to the timestamp just before the DROP:
sudo -u postgres find /var/lib/postgresql/16/main -mindepth 1 -delete sudo -u postgres pgbackrest --stanza=main \ --type=time \ "--target=2026-06-11 10:15:30.123456+00" \ --target-action=promote \ --delta \ restore
What --target-action=promote does: After replaying WAL up to the target time, PostgreSQL promotes the recovered cluster to a normal read-write database. Without this flag, the database stays in read-only recovery mode.
Start PostgreSQL and verify the data is back:
sudo systemctl start PostgreSQLsudo -u postgres psql -c "SELECT * FROM important_orders;"
1 id | customer | amount2----+----------+---------3 1 | Alice | 500.004 2 | Bob | 750.005 3 | Charlie | 1200.006(3 rows)
The table is restored exactly as it was at 10:15:30, before the DROP.
Step 12: Create Restore Points Before Migrations
For planned risky operations like schema migrations and bulk deletes, you can create a named restore point:
12SELECT pg_create_restore_point('before_june_migration');
If something goes wrong, restore to that exact point by name:
sudo systemctl stop postgresql sudo -u postgres pgbackrest --stanza=main \ --type=name \ --target="before_june_migration" \ --target-action=promote \ restore sudo systemctl start postgresql
Named restore points are more reliable than timestamps; a timestamp has rounding effects at the microsecond level.
Pick the Right Backup Repository Location
Where you store your backup repository matters. Here are the storage options for pgBackRest repositories:
| Location |
Pros |
Cons |
| Local filesystem |
Fast, simple setup |
Lost if the server fails |
| Separate server (SSH) |
Off-host, survives server failure |
Needs SSH key setup |
| S3 / S3-compatible |
Durable, off-site, scalable |
Slightly slower, costs money |
| Azure Blob / GCS |
Same benefits as S3 |
Vendor-specific setup |
For production, always store backups off the database server. The same disk failure destroys a backup on the same disk. An affordable Linux VPS makes an excellent dedicated backup repository host, is cost-effective, isolated from your main database server, and easy to access via SSH.
How to Monitor pgBackRest Backups
Never assume backups are working; always check them. View the current backup catalog:
sudo -u postgres pgbackrest info
Check for WAL archiving issues from inside PostgreSQL:
1SELECT2 archived_count,3 failed_count,4 last_archived_wal,5 last_archived_time,6 now() - last_archived_time AS archive_lag7FROM pg_stat_archiver;
Alert on these three metrics:
- Archive lag > 5 minutes: pipeline may be broken.
- failed_count > 0: WAL archiving is failing.
- Backup age > your backup interval with buffer: scheduler may have failed.
For deeper PostgreSQL health, the PostgreSQL autovacuum tuning guide covers how to keep your database in optimal condition, which directly improves recovery speed.
Test pgBackRest Recovery Regularly
A backup that has never been tested is not a backup. Run a monthly recovery test on a separate path:
sudo -u postgres pgbackrest --stanza=main \ --type=time \ "--target=2026-06-01 02:00:00+00" \ --target-action=promote \ --pg1-path=/var/lib/postgresql/16/test_recovery \ restore
After restoring, start a temporary PostgreSQL instance that points to that path and verify that the data looks correct. Record the recovery time; that is your real RTO.
Conclusion
Setting up pgBackRest for PostgreSQL backups and recovery is one of the best choices you can make for any production database. By combining base backups with continuous WAL archiving, pgBackRest gives you the ability to recover to any second in time. Just remember to test your recovery process before you actually need it.
We hope you enjoy this guide. For more information, you can visit the pgBackRest Official User Guide.