Restoring a PostgreSQL backup rebuilds data after accidental deletes, failed migrations, or storage incidents, and it provides a practical way to prove that backups are actually recoverable.
Logical backups created by pg_dump are commonly stored as a plain SQL script (.sql) or as an archive such as custom format (.dump/.backup), tar, or directory. Archive backups are restored with pg_restore, which replays objects in dependency order and supports selective restores and ownership/privilege controls.
A restore can overwrite objects, change ownership and privileges, and require downtime depending on application behavior and locking. Ensure the destination database is correct before starting, confirm the restore role has sufficient permissions, and plan for exclusive access when restoring into an existing production database.
Related: How to create a PostgreSQL database backup \\
Related: How to upgrade PostgreSQL safely
$ sudo -u postgres ls -lh /var/backups/postgresql/appdb-2025-12-29_090421.dump -rw------- 1 postgres postgres 25K Dec 29 09:04 /var/backups/postgresql/appdb-2025-12-29_090421.dump
Backups are commonly stored under /var/backups or a dedicated backup directory with permissions restricted to postgres.
$ sudo -u postgres pg_restore --list /var/backups/postgresql/appdb-2025-12-29_090421.dump ; ; Archive created at 2025-12-29 09:04:22 UTC ; dbname: appdb ; TOC Entries: 61 ; Compression: gzip ; Dump Version: 1.15-0 ; Format: CUSTOM ; ##### snipped #####
Plain SQL backups (.sql) are restored with psql, and pg_restore typically reports input file appears to be a text format dump on those files.
$ sudo -u postgres createdb appdb_restore
Restoring to a new database name avoids overwriting an existing database during testing or validation.
$ sudo -u postgres pg_restore --dbname=appdb_restore --verbose /var/backups/postgresql/appdb-2025-12-29_090421.dump pg_restore: connecting to database for restore pg_restore: creating TABLE "public.events" pg_restore: creating TABLE "public.events_2025_02" pg_restore: creating TABLE "public.events_2025_03" pg_restore: creating TABLE "public.events_default" pg_restore: creating SEQUENCE "public.events_event_id_seq" pg_restore: creating TABLE "public.orders" pg_restore: creating SEQUENCE "public.orders_id_seq" pg_restore: creating SEQUENCE OWNED BY "public.orders_id_seq" pg_restore: creating TABLE "public.users" pg_restore: creating SEQUENCE "public.users_id_seq" pg_restore: creating SEQUENCE OWNED BY "public.users_id_seq" pg_restore: creating TABLE ATTACH "public.events_2025_02" pg_restore: creating TABLE ATTACH "public.events_2025_03" pg_restore: creating TABLE ATTACH "public.events_default" pg_restore: creating DEFAULT "public.orders id" pg_restore: creating DEFAULT "public.users id" pg_restore: processing data for table "public.events_2025_02" pg_restore: processing data for table "public.events_2025_03" pg_restore: processing data for table "public.events_default" pg_restore: processing data for table "public.orders" pg_restore: processing data for table "public.users" pg_restore: executing SEQUENCE SET events_event_id_seq pg_restore: executing SEQUENCE SET orders_id_seq pg_restore: executing SEQUENCE SET users_id_seq pg_restore: creating CONSTRAINT "public.events events_pkey" pg_restore: creating CONSTRAINT "public.events_2025_02 events_2025_02_pkey" pg_restore: creating CONSTRAINT "public.events_2025_03 events_2025_03_pkey" pg_restore: creating CONSTRAINT "public.events_default events_default_pkey" pg_restore: creating CONSTRAINT "public.orders orders_pkey" pg_restore: creating CONSTRAINT "public.users users_pkey" pg_restore: creating INDEX "public.events_event_type_idx" pg_restore: creating INDEX "public.events_2025_02_event_type_idx" pg_restore: creating INDEX "public.events_user_id_idx" pg_restore: creating INDEX "public.events_2025_02_user_id_idx" pg_restore: creating INDEX "public.events_2025_03_event_type_idx" pg_restore: creating INDEX "public.events_2025_03_user_id_idx" pg_restore: creating INDEX "public.events_default_event_type_idx" pg_restore: creating INDEX "public.events_default_user_id_idx" pg_restore: creating INDEX "public.idx_orders_customer_id" ##### snipped #####
Using --clean drops objects before recreating them, and running a restore against the wrong database destroys data.
##### snipped #####</code>
Using --clean drops objects before recreating them, and running a restore against the wrong database destroys data.
$ sudo -u postgres psql -d appdb_restore -c "\dt"
List of relations
Schema | Name | Type | Owner
--------+----------------+-------------------+----------
public | events | partitioned table | postgres
public | events_2025_02 | table | postgres
public | events_2025_03 | table | postgres
public | events_default | table | postgres
public | orders | table | appowner
public | users | table | appowner
(6 rows)
$ sudo -u postgres psql -d appdb_restore -c "SELECT pg_size_pretty(pg_database_size(current_database())) AS size;" size --------- 8132 kB (1 row)
Application configuration may reference a fixed database name, so a restore into a new database can require a connection string update.
Validation should include writes, because restores can affect sequences, permissions, and ownership.