This episode dissects the three pgBackRest backup types — full, differential, and incremental — along with how each works, their sizes, and when to use each one. You'll also configure the retention policy with repo1-retention-full and repo1-retention-diff so backups are automatically expired and the repository never fills up.

Our first full backup was completed in episode 4. The next question: will we run --type=full every day? The answer is no. For a database that keeps growing, daily fulls waste space and time. pgBackRest provides three backup types arranged in tiers, and episode 5 dissects how each works, when to use them, and how the retention policy sweeps away old backups automatically.
Imagine an office archive: a full is a photocopy of every document, a differential is a photocopy of everything that changed since the last full photocopy, and an incremental is a record of changes since the last photocopy activity — no matter how small.
Full copies all cluster data files without exception. It's the only self-contained backup — it doesn't depend on any other backup for restore. Its size is the largest and it takes the longest, which is why it's run infrequently — usually once a week.
sudo -u postgres pgbackrest --stanza=main backup --type=fullDifferential stores all changes since the last full backup, whatever happened in between. Because its reference base is always the full, the differential chain is short and restore is simple: just the full + the last differential.
sudo -u postgres pgbackrest --stanza=main backup --type=diffIncremental only stores the changes since any last backup — which can be a previous incremental. The consequence is that the chain becomes long and tiered; an incremental restore requires the full + the entire chain of incrementals after it.
sudo -u postgres pgbackrest --stanza=main backup --type=incr| Aspect | Full | Differential | Incremental |
|---|---|---|---|
| Contents | All data | Changes since full | Changes since last backup |
| Size | Largest | Medium | Smallest |
| Time | Longest | Medium | Fastest |
| Restore needs | Only itself | Full + itself | Full + entire chain |
| Ideal frequency | Weekly | Daily | More frequent / large data |
Note
An incremental may be small, but every incremental adds one more link to the restore chain. Too many incrementals make restore slow and risky if a single link is corrupted. This is why the classic pattern is weekly full + daily differential — short chain, saved space.
Without retention, the repository grows without bound: every full re-stores all the data, plus WAL keeps flowing in. Retention is the policy that decides "how far back" backups must be kept — the rest is expired automatically.
Add to [global] in /etc/pgbackrest.conf:
[global]
repo1-path = /var/lib/pgbackrest
repo1-retention-full = 2
repo1-retention-diff = 5
repo1-retention-archive-type = full
repo1-retention-archive = 2repo1-retention-full = 2 — always keep at least 2 full backups.repo1-retention-diff = 5 — keep differentials referencing the latest full; differentials from older fulls are expired.repo1-retention-archive-type = full + repo1-retention-archive = 2 — WAL needed to restore up to the last 2 full backups is kept; older WAL is expired along with them.The chosen values should be based on your RPO (Recovery Point Objective) and RTO (Recovery Time Objective) — we'll discuss how to determine them in episode 10.
Expire runs automatically after every successful backup — you don't need to call it manually:
sudo -u postgres pgbackrest --stanza=main backup --type=diff
# output: ... INFO: expire command end: completed successfullypgBackRest computes which backups no longer satisfy retention, then removes them from the repository. Important: backups needed as a chain foundation will not be expired, whatever the retention values — pgBackRest guarantees the chain can always be restored.
To test the policy without waiting for the schedule, expire can be run manually:
sudo -u postgres pgbackrest --stanza=main expireWarning
Retention isn't about "how many backups you want to keep," but "how far back you must be able to recover." If your RPO is 7 days, repo1-retention-full = 1 might be enough — but you lose the ability to recover to the state of 3 weeks ago. Determine the business requirement first, then the retention numbers.
Let's see the effect in practice. Run in sequence:
sudo -u postgres pgbackrest --stanza=main backup --type=full
sudo -u postgres pgbackrest --stanza=main backup --type=diff
sudo -u postgres pgbackrest --stanza=main backup --type=incr
sudo -u postgres pgbackrest infoNotice the labels in info:
full backup: 20260813-090000F
diff backup: 20260813-110000D
incr backup: 20260813-120000IEach new backup shown in info displays a "backup reference" — the list of backups it needs for restore. This is real evidence of the dependency relationship between the types we discussed.
Key takeaways:
repo1-retention-full and repo1-retention-diff control how long backups are kept.In the next episode we'll dissect WAL archiving & archive_command — how archive_mode=on works, archive-push %p, archive-get during restore, what pgbackrest check validates, and when to use async mode to reduce latency on high-write workloads. This is the layer that makes precise PITR possible!