← Back to Blog
TutorialApril 202610 min read

PostgreSQL Backup on Linux: The Complete 2026 Guide

Quick Answer: The most reliable PostgreSQL backup setup on Linux uses pg_dump with the custom format (-Fc), zstd compression, AES-256 encryption, upload to S3, and automated restore verification in Docker. BackupAgent automates this entire pipeline with a single install.

Why PostgreSQL Backup on Linux Requires Care

PostgreSQL's built-in tools — pg_dump and pg_basebackup — are powerful but require careful configuration for production use. The defaults leave out encryption, offsite storage, and restore verification. Teams running bare-metal or VM-based PostgreSQL on Linux need to either wire these pieces together themselves or use a tool that handles it.

This guide covers both paths: the manual shell-script approach and the automated approach.

pg_dump: The Right Flags for Production

pg_dump is the standard logical backup tool. These flags matter for production use:


pg_dump   -Fc                     # Custom format: compressed, parallel-restore capable
  -Z 6                    # Compression level (1-9, 6 is balanced)
  --no-password           # Use .pgpass or PGPASSWORD env var
  -d mydb                 # Database name
  -f /tmp/mydb_$(date +%Y%m%d_%H%M%S).dump

Format Comparison

Format Flag Use Case
Custom -Fc Production backups — parallel restore, selective restore
Directory -Fd Large databases — parallel dump
Plain SQL -Fp Human-readable exports, version migration
Tar -Ft Simple archiving

The custom format is the right choice for production. It compresses internally, supports pg_restore -j for parallel restore (critical for large databases), and allows you to restore specific tables without restoring the full backup.

Setting Up Automated Backups with Cron

A production-grade backup script:


#!/bin/bash
set -euo pipefail

DB="mydb"
BUCKET="s3://my-backup-bucket/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
TMPFILE="/tmp/${DB}_${DATE}.dump"

# Dump
PGPASSWORD="$DB_PASSWORD" pg_dump -Fc -d "$DB" -h localhost -U backup_user -f "$TMPFILE"

# Compress + encrypt (zstd then AES-256)
zstd -q "$TMPFILE" -o "${TMPFILE}.zst"
openssl enc -aes-256-gcm -salt -pass env:BACKUP_KEY -in "${TMPFILE}.zst" -out "${TMPFILE}.zst.enc"

# Upload to S3
aws s3 cp "${TMPFILE}.zst.enc" "${BUCKET}/${DB}_${DATE}.dump.zst.enc"

# Cleanup
rm -f "$TMPFILE" "${TMPFILE}.zst" "${TMPFILE}.zst.enc"

echo "Backup completed: ${DB}_${DATE}"

Add to crontab:


crontab -e
# Run nightly at 2 AM
0 2 * * * PGPASSWORD="secret" BACKUP_KEY="yourkey" /opt/scripts/backup-postgres.sh >> /var/log/pg-backup.log 2>&1

Critical: Create a Dedicated Backup User

Never use the superuser for backups. Create a minimal-permission backup role:


CREATE ROLE backup_user WITH LOGIN PASSWORD 'strongpassword';
GRANT CONNECT ON DATABASE mydb TO backup_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO backup_user;

Testing Your PostgreSQL Backup Restore

This step is non-negotiable. A backup you have never tested is a backup you do not have.

Quick restore test with Docker:


# Decrypt and decompress
openssl enc -d -aes-256-gcm -pass env:BACKUP_KEY   -in backup.dump.zst.enc -out backup.dump.zst
zstd -d backup.dump.zst -o backup.dump

# Start a fresh PostgreSQL container
docker run -d --name pg-restore-test   -e POSTGRES_PASSWORD=test   -e POSTGRES_DB=mydb_test   postgres:16-alpine

# Wait for PostgreSQL to start
sleep 5

# Restore
docker exec -i pg-restore-test pg_restore   -U postgres -d mydb_test < backup.dump

# Verify row count
docker exec pg-restore-test psql -U postgres -d mydb_test   -c "SELECT schemaname, tablename, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 10;"

# Cleanup
docker rm -f pg-restore-test

If you do this manually once a week, you will catch most problems. If you want it done after every backup automatically, BackupAgent handles it without any additional scripts.

Backup Retention and Cleanup

Set a retention policy or storage costs compound:


# Delete S3 backups older than 30 days
aws s3 ls s3://my-backup-bucket/postgres/ |   awk '{print $4}' |   while read key; do
    date=$(echo "$key" | grep -oP 'd{8}')
    if [[ $(date -d "$date" +%s) -lt $(date -d "30 days ago" +%s) ]]; then
      aws s3 rm "s3://my-backup-bucket/postgres/$key"
    fi
  done

Or use S3 Lifecycle rules to automatically expire objects after 30 days.

The Automated Alternative: BackupAgent

The shell-script approach works but has maintenance overhead. Every server needs the script deployed, secrets managed, and cron monitored. When a server is reprovisioned, the backup setup needs to be rebuilt.

BackupAgent replaces all of this with a single install:


curl -fsSL get.backupagent.ai | sh
backupagent register --token YOUR_TOKEN
sudo systemctl enable --now backupagent

It auto-detects PostgreSQL, creates the backup schedule, handles encryption and S3 upload, and runs restore verification after every backup. The dashboard shows backup status across all your servers in one place.

Frequently Asked Questions

What is the best way to backup PostgreSQL on Linux?

Combine pg_dump -Fc, zstd compression, AES-256 encryption, S3 upload, and automated Docker restore verification. BackupAgent automates this entire pipeline as a systemd service.

How do I backup a large PostgreSQL database on Linux?

For databases over 50 GB, use pg_dump with the directory format (-Fd) and the -j flag for parallel dumping. For example: pg_dump -Fd -j 4 -d mydb -f /backup/mydb_dir. This uses 4 parallel workers and completes significantly faster than single-threaded logical backup.

Should I use pg_dump or pg_basebackup for production backups?

Use both. pg_dump for portable logical backups that survive major version upgrades. pg_basebackup for physical backups when you need faster restore of large databases. Most production setups use pg_dump for daily backups and pg_basebackup for weekly snapshots.

Ready to try BackupAgent?

AI-verified database backups in under 5 minutes. Free forever.

Sign Up Free