Learn pgBackRest - Backup Scheduling & Retention Policy
Episode 10 of 23

Learn pgBackRest - Backup Scheduling & Retention Policy

This episode automates backups: scheduling weekly fulls and daily differentials with cron or a systemd timer, while WAL archiving runs real-time via archive_command. You'll also determine the retention policy based on business RPO/RTO and repo1-retention-archive for WAL management in the repository.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

A backup that depends on human memory is a backup that fails on the most important day. In episode 9 we could do recovery manually — but no one should have to remember to run backup. In episode 10 we automate: backup scheduling via cron or a systemd timer, plus a retention policy that determines how long everything survives.

Two time layers work here: WAL archiving running real-time (every WAL segment, all the time) and snapshot backups running on a schedule (weekly full, daily differential). Both are governed by different schedules and shouldn't be treated as the same thing.

Scheduling Backups

The classic pattern balancing space, time, and restore complexity:

  • Full: weekly (for example Sunday at 01:00).
  • Differential: daily, every day except the full schedule.
  • WAL archiving: real-time, set by archive_command — no scheduling needed.

With this pattern, RPO is determined by WAL (almost real-time) and RTO is determined by the size of the last backup that must be restored.

Option 1: Cron

Crontab for the postgres user — not root:

Linuxcrontab -u postgres -e
0 1 * * 0   /usr/bin/pgbackrest --stanza=main --type=full backup
0 1 * * 1-6 /usr/bin/pgbackrest --stanza=main --type=diff backup
  • 0 1 * * 0 — Sunday at 01:00: full.
  • 0 1 * * 1-6 — Monday through Saturday at 01:00: differential.

Note

Use the absolute path of the binary (/usr/bin/pgbackrest) in cron because the postgres user's PATH is often incomplete. Failing with pgbackrest: command not found is the most common cron mistake — and the most confusing one, because the command works when tested manually.

Option 2: Systemd Timer

A systemd timer is more modern: it has logging, dependencies, and systemctl list-timers for auditing. Create two units:

Linux/etc/systemd/system/pgbackrest-full.timer
[Unit]
Description=Weekly full backup pgBackRest
 
[Timer]
OnCalendar=Sun 01:00:00
Persistent=true
 
[Install]
WantedBy=timers.target
Linux/etc/systemd/system/pgbackrest-full.service
[Unit]
Description=Full backup of the main stanza
 
[Service]
Type=oneshot
User=postgres
ExecStart=/usr/bin/pgbackrest --stanza=main --type=full backup

Create a duplicate for differential (pgbackrest-diff.timer/service) with OnCalendar=Mon..Sat 01:00:00, then enable them:

Enable the timers
sudo systemctl daemon-reload
sudo systemctl enable --now pgbackrest-full.timer pgbackrest-diff.timer
systemctl list-timers pgbackrest-*

Persistent=true makes the timer run a missed backup if the machine was off at the scheduled time — an important difference from cron, which simply skips it.

Avoiding Overlap

pgBackRest locks the stanza during a backup — two backups on the same stanza can't run in parallel. Don't schedule full and diff at the same hour. This isn't a design flaw: overlapping backups would confuse the chain and retention.

Determining Retention According to RPO/RTO

RPO and RTO

Before choosing numbers, define two business metrics:

  • RPO (Recovery Point Objective): the maximum amount of data that may be lost. Determined by WAL archiving + archive_timeout. An RPO of "1 minute" means WAL must reach the repository that quickly.
  • RTO (Recovery Time Objective): how fast you must recover. Determined by the last backup's size, parallelism, and storage speed.

Retention answers two derived questions: how far back you must be able to recover, and how long the archive must be kept (for example for auditing).

Retention Configuration

/etc/pgbackrest.conf
[global]
repo1-retention-full = 2
repo1-retention-diff = 7
repo1-retention-archive-type = full
repo1-retention-archive = 2
  • repo1-retention-full = 2 — at least 2 full backups are kept (for example: you can always recover to the state of 1-2 weeks ago).
  • repo1-retention-diff = 7 — differentials referencing the latest full are kept (allowing restore to any day in the current week).
  • repo1-retention-archive-type = full + repo1-retention-archive = 2 — WAL for restoring up to the last 2 fulls is kept; older WAL is expired.

Why repo1-retention-archive Matters

This is the option that's often forgotten. WAL no longer needed to restore still-kept backups is just wasted space — but too little WAL makes restoring to an old backup impossible. repo1-retention-archive aligns the WAL lifetime with the backup lifetime, preventing the repository from filling up with useless WAL.

See the WAL size in the repository
sudo -u postgres pgbackrest info

The wal archive min/max line shows the range of stored WAL. If the range keeps stretching without bound, repo1-retention-archive hasn't been applied correctly.

Warning

Retention done wrong: too short means you can't recover far back; too long means the repository bloats. Start from business requirements (30-day audit? 7-day recovery?) then translate them into retention numbers. Don't copy someone else's numbers without understanding the context.

Verifying the Schedule Is Running

After a week of operation, verify the entire chain:

Audit the scheduled backups
sudo -u postgres pgbackrest info
systemctl list-timers pgbackrest-* | grep -E "NEXT|pgbackrest"
sudo -u postgres pgbackrest --stanza=main check

Now we have a self-running system: real-time WAL, weekly full, daily diff, automatic retention. The next episode makes all of this secure — with encryption.

Conclusion

Key takeaways:

  • WAL archiving runs real-time; snapshot backups are scheduled — two different layers.
  • Cron is simple enough; a systemd timer is more structured with Persistent=true.
  • Use the binary's absolute path in schedules; don't overlap backups of one stanza.
  • RPO is determined by WAL; RTO by backup size — retention follows business needs.
  • repo1-retention-archive prevents the repository from being flooded with unused WAL.

In the next episode we'll secure the backups: encryption & security — encrypting the entire repository contents with repo1-cipher-type=aes-256-cbc and repo1-cipher-pass, managing the passphrase in a secret manager, and protecting the config file with 600 permissions. An unencrypted backup is a liability waiting for an incident!

Learn pgBackRest - Backup Scheduling & Retention Policy | Learn pgBackRest