pg_dump
POSTGRESQL

PostgreSQL Backup and Restore: Complete Tutorial with pg_dump and pg_restore

intro

Learn PostgreSQL backup and restore procedure with this hands-on tutorial. Master pg_dump, pg_restore, and disaster recovery scenarios using real examples.

Tools used in the tutorial
Tool Description Link
PostgreSQL logo POSTGRESQL
THE POSTGRESQL DATABASE

Last month, my team's routine server update turned into a 6-hour outage when our PostgreSQL database became corrupted. We recovered the data, but it highlighted how many teams treat backups as an afterthought—until they need them. This tutorial simulates a real database disaster and walks through the complete recovery process. You'll learn PostgreSQL's backup tools (pg_dump and pg_restore), encounter common restore errors, and practice the troubleshooting techniques that experienced DBAs use.

Setting Up

Before we go into this tutorial, ensure you have these:

  • Docker Desktop or OrbStack installed on your machine.
  • Basic terminal/command line knowledge
  • Basic knowledge of SQL and PostgreSQL

Time to get right into this PostgreSQL backup and restore tutorial!

Step 1: Set Up Your Working Directory

Create a directory for your project and move into it:

Copy
        
1 mkdir ~/postgresql-backup-demo 2 cd ~/postgresql-backup-demo

Then, create the local data directory:

Copy
        
1 mkdir postgresqldata

OrbStack will directly mount that directory.

Step 2: Create the Docker Compose File

Create a file called postgresql.yml:

Copy
        
1 nano postgresql.yml

Then, populate it with this content:

Copy
        
1 version: '3.8' 2 3 services: 4 survey-postgresql: 5 image: postgres:17 6 volumes: 7 - ./postgresqldata:/var/lib/postgresql/data 8 environment: 9 - POSTGRES_USER=dbadmin 10 - POSTGRES_PASSWORD=securepassword123 11 - POSTGRES_DB=postgres 12 ports: 13 - "5432:5432"
populating our yaml file
populating our yaml file

Its worth noting that in production environments, you should use an even stronger authentication mechanism.

Step 3: Create PostgreSQL Container

Start your Docker composer for PostgreSQL server initialization with:

Copy
        
1 docker compose -f postgresql.yml up -d

This is the output you should get:

Copy
        
1 $ docker compose -f postgresql.yml up -d 2 [+] Running 2/2 3 ✔ Network postgresql-backup-demo_default Created 0.0s 4 ✔ Container postgresql-backup-demo-survey-postgresql-1 Started 0.3s

Now, access the database server and log in:

Copy
        
1 docker exec -it $(docker ps -q --filter ancestor=postgres:17) bash
Copy
        
1 psql -U dbadmin -d postgres

Step 4: Build the Survey Platform Database and Create the Survey Data Structure

Create a dedicated role for our application, which is a security best practice:

Copy
        
1 CREATE ROLE demo;

Then, create the sample database that will contain survey info:

Copy
        
1 CREATE DATABASE surveyplatform OWNER demo;

Now, populate the surveyplatform database with a sample respondents table:

Copy
        
1 CREATE TABLE respondents ( 2 id SERIAL PRIMARY KEY, 3 name VARCHAR(255), 4 school_id INTEGER 5 ); 6 7 INSERT INTO respondents (name, school_id) VALUES 8 ('Mark', NULL), 9 ('Jeff', NULL), 10 ('Jude', NULL), 11 ('Kiki', NULL), 12 ('Rodry', NULL), 13 ('Vini', NULL), 14 ('Dahnke', NULL), 15 ('Arafat', NULL), 16 ('Emmanuel', NULL), 17 ('Franco', NULL);

Finally, exit the PostgreSQL process to prepare our backup:

Copy
        
1 \q

Step 5: Creating the Safety Net (The Initial Backup)

This is the crucial moment. Create a backup before disaster strikes using pg_dump:

Copy
        
1 pg_dump -U dbadmin -h localhost -W -F c -f /var/lib/postgresql/data/2025-04-01-original.dump surveyplatform
Copy
        
1 exit

The -F c flag creates a "custom format" backup. This is PostgreSQL's most versatile backup format: compressed, fast to restore, and supports selective recovery. The -W flag prompts for a password, ensuring proper authentication.

Simulating Real-World Database Failure

Now comes the heart-pounding part. We are going to simulate what happens when things go horribly wrong. In the real world, this might be any of these:

  • A server crash
  • Hardware failure
  • Accidental data deletion
  • Software bugs that corrupt data

Simulate that with these commands:

Copy
        
1 # Return to our "crashed" database server 2 docker exec -it $(docker ps -q --filter ancestor=postgres:17) bash 3 4 # Connect as the admin to assess the damage 5 psql -U dbadmin -d postgres
Copy
        
1 -- Simulate catastrophic data loss 2 DROP DATABASE surveyplatform; 3 DROP ROLE demo; 4 5 -- Exit to begin our recovery 6 \q

Imagine for example, your database is gone. All 10 survey respondents, vanished. In a real scenario, this represents potentially millions of dollars in lost business data. Your users can't access the platform and pressure is mounting.

Step 6: The First Recovery Attempt (And Why It Fails)

This is where most people panic. Let's see what happens when we try to restore the data using pg_restore, without understanding PostgreSQL's security model:

Copy
        
1 # Attempt to restore our backup 2 pg_restore -U dbadmin -h localhost -W -d postgres -C /var/lib/postgresql/data/2025-04-01-original.dump

This is the error we would get:

The error message returned by the pg_restore command
The error message returned by the pg_restore command

PostgreSQL tried to restore your database, but it could not complete the operation because the demo role (user account) that owned the database doesn't exist anymore. This is like trying to restore files to a user account that was deleted.

This is what we need to understand when databases crash, sometimes user accounts and permissions get corrupted or lost. Your backup contains not just data, but also ownership and permission information.

Step 7: The Fix — Understanding PostgreSQL Security

This is the methodical approach that experienced DBAs use:

Copy
        
1 # Connect to PostgreSQL to fix the underlying issue 2 psql -d postgres -U dbadmin
Copy
        
1 -- Recreate the missing role (this is the key step most people miss) 2 CREATE ROLE demo; 3 4 -- Check what databases exist (investigating the current state) 5 \l

You might see that surveyplatform was partially created but incomplete. This is normal, as PostgreSQL stopped the restore when it hit the permission error:

Screenshot 2025-09-19 at 4.54.57 PM.png
Screenshot 2025-09-19 at 4.54.57u202fPM.png

Complete the restore procedure with:

Copy
        
1 -- Clean up the incomplete restore attempt 2 DROP DATABASE IF EXISTS surveyplatform; 3 4 -- Exit PostgreSQL 5 \q

It’s appropriate to learn that database recovery is not just about restoring data. It is about recreating the entire environment, including users, permissions, and database structures.

Step 8: The Successful Recovery

Now, for the satisfying part, watch everything come back to life:

Copy
        
1 # Attempt the restore again (with proper setup) 2 pg_restore -U dbadmin -h localhost -W -d postgres -C /var/lib/postgresql/data/2025-04-01-original.dump

Great! Our database is now restored. But did we actually recover everything? Let's verify our survey platform is fully operational:

Copy
        
1 # Connect to verify our recovery 2 psql -d postgres -U dbadmin
Copy
        
1 -- Switch to our restored database 2 \c surveyplatform 3 4 -- Check if our precious survey data survived 5 SELECT * FROM respondents;

The result will be:

Note that the data has been restored as desired
Note that the data has been restored as desired

Victory! You should see all 10 survey respondents, exactly as they were before the disaster. Your survey platform database is back in shape. Crisis averted!

At this point, it is only right that we modify some data to test our backup strategy:

Copy
        
1 -- Simulate a data update (maybe Mark changed his name to Marcus) 2 UPDATE respondents SET name = 'Marcus' WHERE id = 1; 3 4 -- Verify the change 5 SELECT * FROM respondents WHERE id = 1; 6 7 -- Exit PostgreSQL 8 \q 9 exit

Now, we have modified the data. This will help us verify that our various backup formats can restore to specific points in time.

Mastering the Four Backup Formats: The Complete Toolkit

Real database administrators need multiple backup strategies. Each format serves different purposes, and knowing when to use each one can save you hours in emergency situations.

Use CaseBackup Method
Version control systems, manually inspecting or modifying backups before restoring/great for debuggingpg_dump -f /var/lib/postgresql/data/2025-04-01-plaintext.sql -U dbadmin -h localhost -W surveyplatform
Compression, speed, and selective restorationpg_dump -F c -f /var/lib/postgresql/data/2025-04-01-custom.dump -U dbadmin -h localhost -W surveyplatform
Huge databases/parallel processing,pg_dump -F d -f /var/lib/postgresql/data/2025-04-01-directory -U dbadmin -h localhost -W surveyplatform
Easy file management, easy transfer of backups between systemspg_dump -F t -f /var/lib/postgresql/data/2025-04-01-tar.tar -U dbadmin -h localhost -W surveyplatform

Step 9: Creating our Complete Backup Arsenal

Let’s begin by creating our complete backup arsenal:

Copy
        
1 # Access your database server again 2 docker exec -it $(docker ps -q --filter ancestor=postgres:17) bash 3 4 # Create four different backup formats (this is your complete toolkit) 5 # 1. Plain Text SQL Backup (human-readable, great for debugging) 6 pg_dump -f /var/lib/postgresql/data/2025-04-01-plaintext.sql -U dbadmin -h localhost -W surveyplatform 7 8 # 2. Custom Format (compressed, most versatile) 9 pg_dump -F c -f /var/lib/postgresql/data/2025-04-01-custom.dump -U dbadmin -h localhost -W surveyplatform 10 11 # 3. Directory Format (parallel processing for large databases) 12 pg_dump -F d -f /var/lib/postgresql/data/2025-04-01-directory -U dbadmin -h localhost -W surveyplatform 13 14 # 4. TAR Format (easy file management) 15 pg_dump -F t -f /var/lib/postgresql/data/2025-04-01-tar.tar -U dbadmin -h localhost -W surveyplatform

Step 10: Preparing for Restore Tests

First, let's reset our data to the "Marcus" state for testing:

Copy
        
1 psql -d surveyplatform -U dbadmin -h localhost
Copy
        
1 -- Reset the name back to Mark for our restore test 2 UPDATE respondents SET name = 'Mark' WHERE id = 1; 3 4 -- Verify current state 5 SELECT * FROM respondents WHERE id = 1; 6 \q

Now we'll test each restore method to see how they handle our backed-up data (where the name was "Marcus").

Step 11: Testing Plain Text Restore (The Exception)

Try to restore the plain text backup with pg_restore:

Copy
        
1 pg_restore -U dbadmin -h localhost -W -d surveyplatform -c /var/lib/postgresql/data/2025-04-01-plaintext.sql

This is expected to fail with:

testing plain text restore
testing plain text restore

This is because plain text backups require a different tool. In particular, it needs psql instead of pg_restore:

Copy
        
1 # The correct way to restore plain text backups 2 psql -U dbadmin -h localhost -W -d surveyplatform -f /var/lib/postgresql/data/2025-04-01-plaintext.sql

Step 12: Testing Custom Format Restore

Copy
        
1 # This should work flawlessly (the most reliable method) 2 pg_restore -U dbadmin -h localhost -W -d surveyplatform -c /var/lib/postgresql/data/2025-04-01-custom.dump

After running this, verify it worked:

Copy
        
1 # Check if the restore worked 2 psql -d surveyplatform -U dbadmin -h localhost -c "SELECT * FROM respondents WHERE id = 1;"

You should see "Marcus" - proving the backup captured the exact state when we created it.

Note the Marcus output
Note the “Marcus“ output

The final verification showing "Marcus" provides that satisfying moment of proof that everything works correctly.

Tutorial complete!

Conclusion

You've now successfully navigated a complete PostgreSQL disaster recovery scenario. You understand why backups fail, how to fix common restore errors, and most importantly, you've verified that your backup strategy actually works. The custom format backup you've mastered will handle the majority of real-world scenarios you'll encounter as a database administrator.

While command-line tools like pg_dump and pg_restore form the backbone of PostgreSQL backup operations, visual database management tools like DbVisualizer can simplify your daily database administration tasks, from monitoring backup schedules to quickly inspecting restored data structures. The combination of solid backup fundamentals and efficient management tools gives you the complete toolkit for confident database administration.

 Download DbVisualizer for free today, and happy querying!

FAQ

Which PostgreSQL backup format should I use for daily backups?

Use the custom format (-F c) for most situations. It's compressed, fast to restore, and supports selective recovery.

The command is:

Copy
        
1 pg_dump -U username -F c -f backup.dump database_name

Custom format works with pg_restore and handles the majority of real-world backup instances.

How often should I test my PostgreSQL backups?

Test backups monthly at minimum. Create a test database, restore your backup, and verify the data integrity. Many backup failures are only discovered during emergencies.

A simple test: restore to a test database and run SELECT count(*) FROM your_main_table; to verify row counts match your expectations.

Why do I get "role does not exist" errors when restoring backups?

This happens when the database role (user) that owned the original database doesn't exist in the target system. Before restoring, create the missing role:

Copy
        
1 CREATE ROLE rolename;

Then run your pg_restore command.

Can I restore a PostgreSQL backup to a different database name?

Yes. First create the target database:

Copy
        
1 createdb new_database_name

Then, use pg_restore without the -C flag:

Copy
        
1 pg_restore -U username -d new_database_name backup.dump

The -C flag creates the database with the original name, so omit it when restoring to a different database.

Dbvis download link img
About the author
Leslie S. Gyamfi.
Leslie S. Gyamfi
Leslie Gyamfi is a mobile/web app developer with a passion for creating innovative solutions. He is dedicated to delivering high-quality products and technical articles. You can connect with him on LinkedIn
The Table Icon
Sign up to receive The Table's roundup
More from the table
Title Author Tags Length Published
title

SQL Interview Questions and Answers: Part 1 — The Basics

author Lukas Vileikis tags DBMS MARIADB MySQL POSTGRESQL SQL SQL SERVER 10 min 2026-03-30
title

SQL String Functions: Everything You Need to Know

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 13 min 2025-11-24
title

The Best PostgreSQL GUI Tools of 2026: Visual Database Client Comparison

author TheTable tags Database clients GUI POSTGRESQL 7 min 2025-10-29
title

SQL Boolean Type: How to Use It in All Major Relational Databases

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 8 min 2025-09-23
title

Postgres List Schemas: 3 Different Approaches

author Antonello Zanini tags POSTGRESQL 5 min 2025-09-15
title

pgvectorscale: An Extension for Improved Vector Search in Postgres

author Antonello Zanini tags AI POSTGRESQL Vectors 9 min 2025-09-03
title

PostgreSQL Index Mechanism: A Guide to Database Performance Optimization

author Leslie S. Gyamfi tags POSTGRESQL 8 min 2025-09-02
title

UUIDv7 in PostgreSQL 18: What You Need to Know

author Antonello Zanini tags POSTGRESQL 8 min 2025-09-01
title

PostgreSQL ISNULL Equivalent: COALESCE, CASE, and NULLIF

author TheTable tags POSTGRESQL 5 min 2025-08-19
title

SQL IS NOT NULL Condition: Definitive Guide

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 6 min 2025-08-13

The content provided on dbvis.com/thetable, including but not limited to code and examples, is intended for educational and informational purposes only. We do not make any warranties or representations of any kind. Read more here.