Backup Types and Strategy

This note is the PostgreSQL production backup reference for the stoxx-postgres lab. PostgreSQL does not expose SQL Server-style full, differential, and transaction log backup commands. The operational design is still the same problem, but the building blocks differ: a physical base backup establishes a restart baseline, continuous WAL archiving preserves every change after that baseline, and logical dumps provide object-level portability that physical recovery cannot.

Backup strategy decision path

PostgreSQL backup design starts by deciding whether the requirement is physical recovery, point-in-time recovery, or logical portability. The chain below shows how those decisions diverge.

This diagram maps PostgreSQL backup design from recovery target to artifact type and verification path.


flowchart TD
  START["Design goal:<br/>RPO + RTO"] --> PITR{"Need point-in-time recovery?"}
  PITR -->|Yes| PHYS["Physical path:<br/>base backup + archived WAL"]
  PITR -->|No| LOGICAL{"Need object-level portability?"}
  LOGICAL -->|Yes| DUMP["Logical path:<br/>pg_dump / pg_dumpall"]
  LOGICAL -->|No| SNAP["Snapshot or base backup<br/>for full-cluster rollback"]
  PHYS --> VERIFY1["Verify:<br/>manifest + pg_verifybackup"]
  DUMP --> VERIFY2["Verify:<br/>pg_restore -l / test restore"]
  SNAP --> VERIFY1
  VERIFY1 --> STORE["Off-cluster retention:<br/>object storage or backup software"]
  VERIFY2 --> STORE
  STORE --> DRILL["Restore drill"]
  DRILL --> YES["YES<br/>recoverable"]
  DRILL --> NO["NO<br/>design is incomplete"]

  classDef yes fill:#1f3b2d,stroke:#73d13d,color:#c0caf5
  classDef no fill:#4a1f24,stroke:#db4b4b,color:#c0caf5
  class YES yes
  class NO no

Backup Model

PostgreSQL | pg_basebackup / pg_dump | backup type taxonomy

Compare physical recovery artifacts and logical exports

Reach for this material during backup design, before a schedule is written. The operational goal is to map the recovery requirement to the correct PostgreSQL artifact so the schedule preserves the restore path deliberately.

ArtifactWhat it capturesDepends onSupports PITR?Typical use
Physical base backupConsistent copy of the cluster data directoryNothing earlier, but recovery after the backup timestamp requires WALOnly with archived WALBaseline for full-cluster recovery
Archived WALCompleted write-ahead log segments after the base backupA valid base backup and continuous retained WAL chainYesPoint-in-time recovery and replica catch-up
Logical dump (pg_dump)Logical database objects and data for one databaseNo prior chainNoObject-level restore, migration, schema portability
Globals dump (pg_dumpall --globals-only)Roles, role memberships, and tablespacesNo prior chainNoCluster-level objects that pg_dump omits
Storage snapshotFilesystem or volume snapshot of the data directoryStorage system consistency guarantees, often coordinated with PostgreSQL checkpointsSometimes, if paired with WAL retentionInfrastructure-managed rollback or backup acceleration

PostgreSQL has no differential backup analogue

SQL Server differential backups capture changes since the last full backup. PostgreSQL does not have an equivalent built-in backup type. If the design needs recovery to an arbitrary time, the physical chain is base backup + every required WAL segment, not full + diff + logs.

Map the SQL Server model carefully

The closest PostgreSQL equivalent to a SQL Server production chain is:

  • a scheduled physical base backup
  • continuous WAL archiving to durable off-cluster storage
  • optional logical dumps for object-level restore and portability

A schedule built only on pg_dump is useful, but it is not a physical disaster-recovery plan.

PostgreSQL | pg_settings | recovery prerequisites and WAL posture

Inspect the current cluster settings that govern recoverability

Use this query at the start of every backup design review. It is read-only and answers whether the cluster is currently capable of physical backups, whether continuous archiving is active, and whether the WAL surface is configured for recovery rather than only for crash safety.

SELECT name, setting, unit, source
FROM pg_settings
WHERE name IN (
  'archive_mode',
  'archive_command',
  'archive_timeout',
  'checkpoint_timeout',
  'full_page_writes',
  'max_replication_slots',
  'max_wal_senders',
  'wal_compression',
  'wal_level'
)
ORDER BY name;
namesettingunitsource
archive_command(disabled)default
archive_modeoffdefault
archive_timeout0sdefault
checkpoint_timeout300sdefault
full_page_writesondefault
max_replication_slots10default
max_wal_senders10default
wal_compressionoffdefault
wal_levelreplicadefault

The current lab can take a physical base backup immediately, because wal_level = replica and WAL senders are available. It is not yet point-in-time recoverable after the base-backup timestamp, because archive_mode = off and archive_command is disabled. That distinction matters operationally: a base backup without retained WAL is only a restart baseline at one instant, not a continuous recovery design.

SettingCurrent valueOperational meaningAction
archive_modeoffCompleted WAL segments are not being archived anywhereTurn on before claiming PITR coverage
archive_command(disabled)No archive target is configuredSet a durable target and test it
wal_levelreplicaPhysical replication and WAL archiving semantics are availableGood baseline for physical recovery
max_wal_senders10Cluster can support backup/replication sender processesAdequate for the lab
full_page_writesonRecovery has torn-page protectionKeep enabled in production

Backup Metadata Inspection

PostgreSQL | backup_label / backup_manifest | physical backup evidence

Inspect the physical artifact produced by pg_basebackup

The lab base backup was taken with pg_basebackup into /tmp/note07/basebackup. backup_label records the exact WAL start point and timeline of the artifact, while backup_manifest records checksummed file inventory for verification.

cat /tmp/note07/basebackup/backup_label
START WAL LOCATION: 0/5000028 (file 000000010000000000000005)
CHECKPOINT LOCATION: 0/5000060
BACKUP METHOD: streamed
BACKUP FROM: primary
START TIME: 2026-04-18 23:12:58 UTC
LABEL: note07_basebackup
START TIMELINE: 1
ls -lh /tmp/note07/basebackup/backup_manifest /tmp/note07/basebackup/backup_label
filesize
backup_label217 B
backup_manifest193 KB
head -n 8 /tmp/note07/basebackup/backup_manifest
{ "PostgreSQL-Backup-Manifest-Version": 1,
"Files": [
{ "Path": "backup_label", "Size": 217, "Last-Modified": "2026-04-18 23:12:58 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "4b52529b" },
{ "Path": "postgresql.conf", "Size": 29950, "Last-Modified": "2026-04-18 20:32:24 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "7c03416c" },
{ "Path": "pg_ident.conf", "Size": 2640, "Last-Modified": "2026-04-18 20:32:24 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "0ce04d87" },
{ "Path": "pg_xact/0000", "Size": 8192, "Last-Modified": "2026-04-18 23:06:14 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "b6dd5df8" },
{ "Path": "base/5/3455", "Size": 16384, "Last-Modified": "2026-04-18 22:51:09 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "7eb64bfe" },

The manifest is the PostgreSQL equivalent of “prove the backup file is structurally complete”. It does not prove business-level recoverability by itself, but it gives the checksum inventory that pg_verifybackup relies on.

PostgreSQL | pg_stat_archiver | archive success and failure counters

Read the current WAL archive posture

This query answers whether WAL archiving is active and whether recent archive attempts have succeeded or failed. It is the closest native visibility surface PostgreSQL offers for continuous physical backup health.

SELECT archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset
FROM pg_stat_archiver;
archived_countlast_archived_wallast_archived_timefailed_countlast_failed_wallast_failed_timestats_reset
002026-04-18 20:37:33.679131+00

archived_count = 0 is expected here only because archiving is disabled. In a production design that claims PITR readiness, a permanently zero archive counter is a defect, not a neutral state.

PostgreSQL | pg_current_wal_lsn() | current WAL identity

Capture the live WAL position of the cluster

The current WAL location is the anchor for reasoning about how far the cluster has advanced beyond the base backup and which WAL file names should exist in durable storage once archiving is enabled.

SELECT now() AS captured_at, pg_current_wal_lsn() AS current_wal_lsn, pg_walfile_name(pg_current_wal_lsn()) AS current_wal_file;
captured_atcurrent_wal_lsncurrent_wal_file
2026-04-18 23:12:00.101795+000/46CEDD8000000010000000000000004

The later base backup started at WAL file 000000010000000000000005, which is consistent with the cluster advancing between the pre-capture and the base-backup checkpoint.

PostgreSQL | pg_replication_slots | cleanup after physical backup

Confirm that no orphaned backup slot remains

pg_basebackup -X stream creates a temporary replication slot unless told otherwise. That slot should disappear automatically when the backup completes. Leaving a slot behind can pin WAL and create silent storage growth.

SELECT slot_name, slot_type, active, restart_lsn, wal_status
FROM pg_replication_slots
ORDER BY slot_name;
slot_nameslot_typeactiverestart_lsnwal_status
(0 rows)

No residual slot remains after the lab backup. That is the expected clean state.


Backup Verification

PostgreSQL | pg_verifybackup | verify the physical backup directory

Validate the base backup against its manifest

The container has pg_verifybackup, but not on the default PATH, so the full binary path is used here. This command checks that the backup directory matches the recorded manifest and that required files are present and readable.

/usr/lib/postgresql/16/bin/pg_verifybackup /tmp/note07/basebackup
backup successfully verified

pg_verifybackup proves that the physical artifact is internally coherent. It does not prove that the restore procedure, archive retention, or application cutover runbook is correct. That proof comes only from an actual restore drill.

PostgreSQL | pg_restore -l | inspect a logical dump without restoring it

Read the catalog of objects stored inside the custom-format dump

pg_restore -l is the logical equivalent of “inspect the backup header before using it”. It does not restore data. It lists the table of contents of the archive so the operator can confirm that the dump contains the expected schemas and objects.

pg_restore -l /tmp/note07/stoxx_note07.dump | head -n 18
;
; Archive created at 2026-04-18 23:13:12 UTC
;     dbname: stoxx
;     TOC Entries: 135
;     Compression: gzip
;     Dump Version: 1.15-0
;     Format: CUSTOM
;     Integer: 4 bytes
;     Offset: 8 bytes
;     Dumped from database version: 16.13 (Debian 16.13-1.pgdg13+1)
;     Dumped by pg_dump version: 16.13 (Debian 16.13-1.pgdg13+1)
;
6; 2615 24577 SCHEMA - bronze postgres
7; 2615 24578 SCHEMA - dbo postgres
8; 2615 24579 SCHEMA - demo_stc postgres
9; 2615 24580 SCHEMA - gold postgres
10; 2615 24581 SCHEMA - silver postgres

The dump contains the expected layered schemas and was created by the same PostgreSQL major version as the source cluster. That is a strong baseline for logical restorability, but a real import test is still the final proof.

PostgreSQL | filesystem inventory | confirm the artifact footprint

Measure the physical and logical backup outputs

Artifact size is not just a storage question. It also determines transfer windows, object-storage upload time, and restore staging time.

du -sh /tmp/note07/basebackup
find /tmp/note07/basebackup -maxdepth 1 -type f | sed 's|/tmp/note07/basebackup/||' | sort
ls -lh /tmp/note07/stoxx_note07.dump
artifactobserved footprint
basebackup/84M
top-level filesPG_VERSION, backup_label, backup_manifest, pg_hba.conf, pg_ident.conf, postgresql.auto.conf, postgresql.conf
stoxx_note07.dump5.6M

The lab size difference is the reason PostgreSQL operators often retain both artifact classes: the physical backup is the recovery baseline, while the logical dump is the compact object-level export.


Production Backup Commands

PostgreSQL | pg_basebackup | take a physical base backup

Take a conventional base backup with streamed WAL

This command is the PostgreSQL baseline equivalent of taking a conventional full database backup. It copies the cluster files and streams enough WAL to make the copy consistent.

pg_basebackup -U postgres -D /tmp/note07/basebackup -Fp -X stream -c fast -l note07_basebackup -v
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/5000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_1542"
pg_basebackup: write-ahead log end point: 0/5000100
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

Key choices in this capture:

FlagMeaningWhy it matters
-Fpplain-format directory backupkeeps backup_manifest directly readable
-X streamstream WAL during the backupavoids depending on an external WAL archive for backup consistency
-c fastrequest a fast checkpointshortens the wait to start the backup at the cost of more immediate I/O
-l note07_basebackupwrite a readable labelmakes later artifact inspection faster

PostgreSQL | pg_dump | create a logical database backup

Write a compressed custom-format dump of stoxx

pg_dump is not a substitute for continuous physical recovery. Its strength is object-level restore, portability, and the ability to inspect or restore selectively with pg_restore.

pg_dump -U postgres -d stoxx -Fc -f /tmp/note07/stoxx_note07.dump
ls -lh /tmp/note07/stoxx_note07.dump
filesize
/tmp/note07/stoxx_note07.dump5.6M

PostgreSQL | pg_dumpall --globals-only | capture roles and other cluster-level objects

Protect the cluster objects that per-database dumps omit

Per-database dumps do not contain roles, role memberships, or tablespace definitions. A backup strategy that restores data but loses login and privilege state is still incomplete.

pg_dumpall -U postgres --globals-only > /tmp/note07/globals_only.sql
ls -lh /tmp/note07/globals_only.sql
filesize
/tmp/note07/globals_only.sql671B
SELECT rolname, rolsuper, rolcreatedb, rolreplication
FROM pg_roles
ORDER BY rolname;
rolnamerolsuperrolcreatedbrolreplication
pg_checkpointfff
pg_create_subscriptionfff
pg_database_ownerfff
pg_execute_server_programfff
pg_monitorfff
pg_read_all_datafff
pg_read_all_settingsfff
pg_read_all_statsfff
pg_read_server_filesfff
pg_signal_backendfff
pg_stat_scan_tablesfff
pg_use_reserved_connectionsfff
pg_write_all_datafff
pg_write_server_filesfff
postgresttt

PostgreSQL | pg_controldata | checkpoint metadata for backup reasoning

Read the cluster control file after the backup

pg_controldata is not a backup command, but it is a useful control-plane check when reconciling backup labels, checkpoint state, and the current WAL timeline.

/usr/lib/postgresql/16/bin/pg_controldata /var/lib/postgresql/data | egrep 'Database cluster state|Latest checkpoint location|Latest checkpoint.s TimeLineID|Latest checkpoint.s REDO WAL file'
Database cluster state:               in production
Latest checkpoint location:           0/5000060
Latest checkpoint's REDO WAL file:    000000010000000000000005
Latest checkpoint's TimeLineID:       1

The checkpoint location and redo WAL file line up with the backup_label captured earlier. That consistency is what an operator wants to see when validating a freshly created physical artifact.


Object Storage Backup with GCS

GCP | Cloud Storage | validate the current upload surface

Confirm CLI context and bucket readiness before an upload

The host workstation has gcloud installed and authenticated. That is necessary, but not sufficient. The target bucket also has to exist and be writable before any base backup or dump can leave the machine.

gcloud --version | Select-Object -First 1
gcloud auth list --filter=status:ACTIVE --format="value(account)"
gcloud config get-value project
gcloud storage ls gs://stoxx-sql-bucket
Google Cloud SDK 563.0.0
alexper.recovery@gmail.com
bq-wh-nb
ERROR: (gcloud.storage.ls) gs://stoxx-sql-bucket not found: 404.

The current lab host can talk to GCP, but the bucket path used in the mirrored SQL Server note is not present in project bq-wh-nb. In PostgreSQL terms, that means off-cluster retention is not operational yet even though the backup artifacts can already be created locally.

PostgreSQL | artifact packaging | prepare immutable files for object upload

Compress and checksum the backup artifacts before shipping them

Because PostgreSQL writes files rather than streaming natively to Cloud Storage, packaging and checksumming are part of the operator workflow unless a higher-level backup tool handles them automatically.

cd /tmp/note07
tar -czf basebackup-20260418.tar.gz basebackup
sha256sum basebackup-20260418.tar.gz stoxx_note07.dump
ls -lh basebackup-20260418.tar.gz stoxx_note07.dump
c047a650af1a9c4b7bdc568f3384004f5697a4142cd34de6381b30e0f7be310f  basebackup-20260418.tar.gz
98102d49afce90aa65fe397187882b6e9f402334e155944c6a9426e7f9415816  stoxx_note07.dump
-rw-r--r-- 1 root     root      13M Apr 18 23:14 basebackup-20260418.tar.gz
-rw-r--r-- 1 postgres postgres 5.6M Apr 18 23:13 stoxx_note07.dump

These are the objects a Cloud Storage upload would actually carry. The important PostgreSQL discipline is to preserve the artifact immutably and to keep its manifest or checksum with it.

Upload patternPostgreSQL fitNotes
gcloud storage cp of packaged artifactsGood for simple labs and one-off exportsDatabase engine is not aware of retention or upload success
WAL-G, pgBackRest, BarmanBest for production physical backups and WAL archivesHandles archiving, retention, and restore orchestration more safely
Snapshot-only with no WAL archiveLimitedAcceptable only when PITR is not required

Scheduling and Retention

PostgreSQL | strategy | match cadence to the recovery target

Choose cadence by workload profile

Workload profilePhysical backup cadenceWAL archive cadenceLogical dump cadenceTypical outcome
Local dev or disposable labWeekly or before risky changesOptionalOn demandFast rollback, no PITR guarantee
Small production DB with moderate RPONightly base backupContinuous WAL archiveDaily custom dumpFull-cluster recovery plus object-level export
High-change production DB with low RPODaily base backup, sometimes more frequent after large maintenance eventsContinuous WAL archive with durable off-cluster retentionDaily or release-alignedPITR measured in minutes, not days

If the design requires “restore to 09:37”, WAL archiving is mandatory. If the design only requires “recover last night’s state”, a base backup and periodic logical dump may be sufficient, but that should be stated explicitly rather than implied.

PostgreSQL | strategy | apply 3-2-1 to physical and logical artifacts

Separate local recovery copies from off-cluster retention

CopyExample in PostgreSQL termsPurpose
1. Local operational copyRecent base backup on fast local or nearby storageFast restore staging
2. Off-cluster durable copyTarred base backup plus WAL archive in object storage or backup repositorySurvive host or volume loss
3. Separate format or platform copyLogical dump and globals dumpObject-level recovery and migration safety

The live lab artifacts illustrate why the copies can differ by purpose: the physical base backup is 84M, its packaged tarball is 13M, and the logical dump is 5.6M. Smaller artifacts are easier to distribute, but they do not replace the physical recovery chain.


Operational Safeguards

PostgreSQL | strategy | restore drills and validation cadence

Structure the restore proof, not just the backup job

At minimum, a PostgreSQL restore drill should prove:

CheckWhy it matters
The physical base backup verifies with pg_verifybackupProves artifact completeness against its manifest
The logical dump inventory is readable with pg_restore -lProves the archive format is intact
Roles and other globals are backed up separatelyPrevents a restore with missing login or privilege state
WAL archive retention spans the desired RPO windowPrevents false confidence about PITR
A real restore boots and accepts connectionsProves the runbook, not just the files

PostgreSQL | strategy | common backup design failures and remediation

Diagnose the most common PostgreSQL backup mistakes

SymptomLikely causeCorrective action
Base backup exists but PITR is impossiblearchive_mode = off or no durable WAL archiveEnable archiving, test archive_command, and monitor pg_stat_archiver
Logical restore works but permissions are wrongpg_dump taken without a globals dumpCapture pg_dumpall --globals-only and restore it first
pg_wal grows without bound after backup activityorphaned replication slot or archive failureInspect pg_replication_slots, archive status, and slot retention
Upload job fails before off-cluster retention beginsbucket or IAM not readyValidate object-storage path and credentials before relying on it
Backup verified locally but restore still failsno restore drill or missing WAL timeline handlingRehearse the exact restore sequence on an isolated target

The current lab already shows one of these safeguards in action: backup creation works, but GCS retention is not ready because gs://stoxx-sql-bucket does not exist. That is precisely the kind of boundary an operator needs to detect before declaring the backup strategy complete.

Next: 08-postgresql-restore-and-recovery turns these artifacts into actual restore procedures and recovery decision points.