$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[April 13, 2026] PostgreSQL

PostgreSQL Backup and Recovery

PostgreSQL Backup: Tips & Tricks

────────────────────────────────────────────────────────

PostgreSQL Backup: Tips & Tricks

Tip: pg_dump for Logical Backups

pg_dump -U myuser -d myapp -Fc > backup.dump

The -Fc format is compressed and supports selective restore.

Gotcha: pg_dump Doesn't Lock Tables

But it does see a consistent snapshot. Long-running dumps can hold back VACUUM.

Tip: Automated Backup Script

#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump -U myuser -d myapp -Fc > /backups/myapp_$DATE.dump
find /backups -name '*.dump' -mtime +7 -delete

Run daily via cron.

Gotcha: Restore Needs the Same Extensions

pg_restore -U myuser -d myapp backup.dump

If the dump includes extensions, they must be available on the target server.

Tip: Point-in-Time Recovery

wal_level = replica
archive_mode = on
archive_command = 'cp %p /archive/%f'

With WAL archiving, you can restore to any point in time.

Gotcha: pg_dump Doesn't Include Roles

Roles and permissions are cluster-level. Back them up separately:

pg_dumpall --roles-only > roles.sql

Tip: Spatie Laravel Backup Package

composer require spatie/laravel-backup
php artisan backup:run

Handles database + files + cleanup scheduling.

Gotcha: Test Your Backups

A backup you haven't tested is not a backup. Restore to a staging database regularly.

Tip: EXPLAIN (ANALYZE, BUFFERS) Is Your Best Friend

For query debugging, always use EXPLAIN (ANALYZE, BUFFERS) instead of plain EXPLAIN. The BUFFERS option shows hit/miss rates for every node, revealing whether your indexes are actually in memory.

Tip: Partial Indexes Are Underutilized

CREATE INDEX ON orders (status) WHERE status = 'pending' creates a tiny index that covers only the rows your query needs. It's faster to scan and cheaper to maintain than a full-column index.

Gotcha: NULL Sorting Is Non-Obvious

By default, NULLs sort AFTER non-null values in ascending order. ORDER BY col DESC puts NULLs FIRST. Use NULLS LAST or NULLS FIRST to be explicit.

Senior Insight

PostgreSQL backup strategies depend entirely on your recovery requirements. pg_dump is reliable for logical backups, but restoring a multi-terabyte database can take days. I've adopted a layered approach: daily pg_dump for schema and small tables, continuous WAL archiving for point-in-time recovery, and physical replication for failover. Test your restore procedure quarterly — an untested backup is not a backup.

Source: pganalyze Blog (https://pganalyze.com/blog), PostgreSQL Docs (https://www.postgresql.org/docs/current/), Crunchy Data Blog (https://www.crunchydata.com/blog)

────────────────────────────────────────────────────────
<-- back to posts