π PostgreSQL Logical Backups β Complete Guide
Hey there! I'm Dhanush K S, an Electronics and Communication Engineering graduate from Bengaluru. I'm passionate about web development, DevOps, and staying up-to-date with the latest technologies. I love documenting my learning journey to help others and reinforce my own knowledge. Hands-on learning is my go-to approach as it allows me to personalize my education. Join me as I share my practical experiences and the lessons I've learned. Feel free to connect with me for collaboration or to share your insights!
When running PostgreSQL in production, taking backups is one of the most critical responsibilities of a DBA or DevOps engineer. Without a proper backup and restore strategy, you risk data loss, corruption, and sleepless nights.
PostgreSQL offers two main categories of backups:
Physical Backups β file-level copies of the database cluster (e.g.,
pg_basebackup).Logical Backups β SQL-based exports that describe database objects and their data.
In this blog, weβll focus on Logical Backups and understand their types, usage, and practical examples.
πΉ What is a Logical Backup?
A logical backup stores the schema (tables, indexes, functions, etc.) and data in a format such as SQL or a compressed archive. Instead of copying files, it extracts database contents into a portable format that can be restored later.
Logical backups are ideal when you want:
To migrate a database from one server to another.
To take backups at the database level instead of the entire cluster.
To move between PostgreSQL versions (e.g., from 13 β 17).
To export/import data for development or testing.
πΉ Two Types of Logical Backups in PostgreSQL
1. pg_dump β Single Database Backup
pg_dump is used to take a backup of a single database.
Example:
pg_dump -U postgres paymentdb | gzip > /backups/paymentdb_$(date +%F).sql.gz
Only
paymentdbschema + data are dumped.It does not include roles, permissions, or other databases.
Restore:
Create the target database:
createdb paymentdbRestore into it:
gunzip -c /backups/paymentdb_2025-09-14.sql.gz | psql -U postgres -d paymentdb
βοΈ Use this when:
You only care about one database in a cluster.
Migrating or sharing a single appβs data.
β Limitation:
- Users/roles and cluster-wide settings must be recreated manually.
2. pg_dumpall β Entire Cluster Backup
pg_dumpall backs up all databases and global objects like roles and tablespaces.
Example:
pg_dumpall -U postgres | gzip > /backups/all_databases_$(date +%F).sql.gz
Includes all databases (
postgres,template1,paymentdb, etc.).Includes
CREATE ROLE,CREATE DATABASE, and permissions.
Restore:
Simply run:
gunzip -c /backups/all_databases_$(date +%F).sql.gz | psql -U postgres
βοΈ Use this when:
You want a full cluster backup including users and permissions.
Migrating entire PostgreSQL instances to a new server.
β Limitation:
- Not suitable for very large databases (SQL dump can be huge and slow).
pg_dumpβ must create DB manually before restore and also user with required premisssions.pg_dumpallβ no need, it creates databases and roles inside the dump.pg_dumpallonly supports plain-text SQL output.Reason: itβs just a wrapper that runs
pg_dumpfor each DB and stitches them together with roles, global objects, etc.So if you want custom format (
-Fc), you must usepg_dumpfor each database separately.
π Thatβs why in production:
For single DB backups β
pg_dump -Fc.For all DBs + roles β use
pg_dumpall(plain SQL)πΉ What is
-Fc?-F= formatc= custom
So-Fc= Custom format dump.
(Other formats are-Fp= plain SQL,-Ft= tar)
πΉ What is Parallel Restore?
Normal restore (
psql < dump.sqlorpg_restore) runs single-threaded.With custom format, you can tell
pg_restoreto use multiple jobs:
pg_dump -Fc mydb > mydb.dump
pg_restore -U postgres -d mydb -j 4 mydb.dump
-j 4β runs 4 parallel worker jobs.This splits the restore across schemas, tables, and indexes simultaneously.
Very useful for large DBs (GBβTB range) because index creation and data load happen faster.
π― Formats in pg_dump
When you run pg_dump, you can specify format using the -F flag:
| Option | Format | File extension | Features | Restore tool | Recommended for |
-Fp | Plain SQL (default) | .sql | Human-readable SQL script with CREATE and INSERT statements. Easy to inspect & edit, but slow restore for big DBs. | psql | Small DBs, debugging, portability |
-Fc | Custom format | .dump | Compressed, compact, allows parallel restore (pg_restore -j), selective restore (only certain tables/schemas). | pg_restore | Production backups, large DBs, faster restore |
-Fd | Directory format | Directory with multiple files | Each table dumped separately β parallel restore & parallel dump. Can restore only parts. Not a single file β harder to move around. | pg_restore | Very large DBs, when parallel dump/restore speed is critical |
-Ft | Tar archive | .tar | Standard tar file. Can be compressed by external tools. Supports pg_restore. Doesnβt support parallel restore. | pg_restore | When you want one portable archive file but still need pg_restore features |
β Recommended Practice
For everyday production backups β
-Fc(Custom format) is recommended:Itβs compressed
Easy to store & move
Works with
pg_restore(fast, parallel, selective restore)
For very large databases (hundreds of GBs β TBs) β
-Fd(Directory format) with multiple jobs (-j) is best.Use
-Fp(plain SQL) only when:DB is small
You need human-readable SQL
Or migrating to another DB system
-Ft(tar) is rarely used in practice β only if you specifically want a tarball for archiving.
π So, best recommendation:
pg_dump -U postgres -Fc mydb > mydb.dump(for most cases)Or
pg_dump -U postgres -Fd backupdir -j 4 mydb(for very large DBs with parallelism)
β So summary:
pg_dumpall = only plain text (no
-Fc).pg_dump -Fc = custom format, supports parallel restore with
pg_restore -j.Plain SQL format (default, no
-Foption)Output is just SQL commands.
Usually named with
.sqlExample:
pg_dump -U postgres -C myappdb > myappdb.sql createdb -U postgres myappdb psql -U postgres -d myappdb -f myappdb.sql
Custom format (-Fc)
Output is a compressed, binary archive.
Usually named with
.dump(or.backup).Needs
pg_restorefor restore.Example:
pg_dump -U postgres -Fc myappdb > myappdb.dump pg_restore -U postgres -d myappdb myappdb.dump
Directory format (
-Fd)Output is a directory containing one file per table.
Restore with
pg_restore.
Tar format (
-Ft)Output is a tar archive.
Restore with
pg_restore.
π So:
If you use default SQL dump β
.sqlmakes sense.If you use custom format (-Fc) β
.dump(or.backup) is the convention.Use
.sqlif you want portability, readability, or to quickly replay commands.Use
.dumpif you want faster backup/restore and advanced options (pg_restore --jobs=Nfor parallel restore).