Back to Blog

PostgreSQL Backup Strategies: Complete Implementation Guide

TheDBAdmin Team5 min read
PostgreSQL Backup Strategies: Complete Implementation Guide

Master PostgreSQL backup and recovery with this comprehensive guide covering logical backups, physical backups, point-in-time recovery, and automated backup strategies.

A robust backup strategy is crucial for protecting your PostgreSQL databases. This guide covers comprehensive backup approaches, automation, and recovery procedures.

Table of Contents

  1. Backup Types
  2. Physical Backup
  3. Logical Backup
  4. Point-in-Time Recovery
  5. Automated Backup Solutions
  6. Backup Verification

Backup Types

Comparison of Backup Methods

plaintext
Physical Backup (pg_basebackup):
+ Fast for large databases
+ Includes all database objects
+ Supports point-in-time recovery
- Requires same PostgreSQL version
- Platform/architecture dependent

Logical Backup (pg_dump):
+ Version independent
+ Platform independent
+ Selective backup possible
- Slower for large databases
- No point-in-time recovery

Physical Backup

Basic pg_basebackup

bash
#!/bin/bash
# backup_physical.sh

BACKUP_DIR="/backup/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="${BACKUP_DIR}/base_${DATE}"

# Create backup directory
mkdir -p ${BACKUP_PATH}

# Perform base backup
pg_basebackup \
    -h localhost \
    -U postgres \
    -D ${BACKUP_PATH} \
    -Ft \
    -X fetch \
    -P \
    -z

# Cleanup old backups (keep last 7 days)
find ${BACKUP_DIR} -type d -mtime +7 -exec rm -rf {} +

WAL Archiving Configuration

ini
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
archive_timeout = 60

# Recovery configuration
restore_command = 'cp /archive/%f %p'
recovery_target_timeline = 'latest'

Continuous Archiving Script

bash
#!/bin/bash
# archive_wal.sh

ARCHIVE_DIR="/archive/postgresql/wal"
MAX_AGE_DAYS=7

# Archive cleanup
find ${ARCHIVE_DIR} -type f -mtime +${MAX_AGE_DAYS} -delete

# Check archive size
ARCHIVE_SIZE=$(du -sh ${ARCHIVE_DIR} | cut -f1)
echo "Current archive size: ${ARCHIVE_SIZE}"

# Alert if archive is getting too large
if [ $(du -s ${ARCHIVE_DIR} | cut -f1) -gt 1000000000 ]; then
    echo "WARNING: WAL archive is larger than 1GB" | mail -s "WAL Archive Alert" [email protected]
fi

Logical Backup

Custom pg_dump Script

bash
#!/bin/bash
# backup_logical.sh

# Configuration
BACKUP_DIR="/backup/postgresql/logical"
POSTGRES_USER="postgres"
POSTGRES_HOST="localhost"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

# Ensure backup directory exists
mkdir -p ${BACKUP_DIR}

# Function to backup a single database
backup_database() {
    local db_name=$1
    local backup_file="${BACKUP_DIR}/${db_name}_${DATE}.custom"
    
    echo "Backing up database: ${db_name}"
    pg_dump \
        -h ${POSTGRES_HOST} \
        -U ${POSTGRES_USER} \
        -Fc \
        -v \
        -f ${backup_file} \
        ${db_name}
    
    # Compress backup
    gzip ${backup_file}
    
    echo "Backup completed: ${backup_file}.gz"
}

# Get list of databases
databases=$(psql -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -t -c "SELECT datname FROM pg_database WHERE datname NOT IN ('template0', 'template1', 'postgres')")

# Backup each database
for db in ${databases}; do
    backup_database ${db}
done

# Cleanup old backups
find ${BACKUP_DIR} -type f -mtime +${RETENTION_DAYS} -delete

# Verify backups
echo "Verifying recent backups..."
for backup in ${BACKUP_DIR}/*_${DATE}.custom.gz; do
    gunzip -t ${backup} || echo "WARNING: Backup verification failed for ${backup}"
done

Selective Table Backup

bash
#!/bin/bash
# backup_tables.sh

# Configuration
TABLES=(
    "users"
    "orders"
    "products"
)
DB_NAME="your_database"
BACKUP_DIR="/backup/postgresql/tables"
DATE=$(date +%Y%m%d_%H%M%S)

# Backup specific tables
for table in "${TABLES[@]}"; do
    echo "Backing up table: ${table}"
    pg_dump \
        -h localhost \
        -U postgres \
        -t ${table} \
        -Fc \
        -v \
        -f "${BACKUP_DIR}/${DB_NAME}_${table}_${DATE}.custom" \
        ${DB_NAME}
done

Point-in-Time Recovery

PITR Configuration

ini
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
archive_timeout = 60

# Recovery time configuration
recovery_target_time = '2025-01-30 12:05:06+05:30'
recovery_target_inclusive = true
recovery_target_timeline = 'latest'

Recovery Script

bash
#!/bin/bash
# perform_pitr.sh

# Configuration
BACKUP_DIR="/backup/postgresql"
ARCHIVE_DIR="/archive/postgresql/wal"
DATA_DIR="/var/lib/postgresql/14/main"
RECOVERY_TIME="2025-01-30 12:05:06+05:30"

# Stop PostgreSQL
systemctl stop postgresql

# Clear data directory
rm -rf ${DATA_DIR}/*

# Restore base backup
latest_backup=$(ls -t ${BACKUP_DIR}/base_* | head -1)
tar xf ${latest_backup} -C ${DATA_DIR}

# Create recovery.signal
touch ${DATA_DIR}/recovery.signal

# Create recovery configuration
cat > ${DATA_DIR}/postgresql.auto.conf << EOF
restore_command = 'cp ${ARCHIVE_DIR}/%f %p'
recovery_target_time = '${RECOVERY_TIME}'
recovery_target_inclusive = true
recovery_target_timeline = 'latest'
EOF

# Start PostgreSQL
systemctl start postgresql

# Monitor recovery progress
while true; do
    if psql -U postgres -c "SELECT pg_is_in_recovery();" | grep -q "f"; then
        echo "Recovery completed"
        break
    fi
    sleep 10
done

Automated Backup Solutions

Automated Backup System

python
# backup_manager.py
import schedule
import time
import subprocess
import logging
from datetime import datetime

class PostgreSQLBackupManager:
    def __init__(self):
        self.logger = logging.getLogger('backup_manager')
        self.setup_logging()
        
    def setup_logging(self):
        logging.basicConfig(
            filename='/var/log/postgresql/backup.log',
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
    
    def perform_physical_backup(self):
        try:
            subprocess.run(['/scripts/backup_physical.sh'], check=True)
            self.logger.info('Physical backup completed successfully')
        except subprocess.CalledProcessError as e:
            self.logger.error(f'Physical backup failed: {str(e)}')
    
    def perform_logical_backup(self):
        try:
            subprocess.run(['/scripts/backup_logical.sh'], check=True)
            self.logger.info('Logical backup completed successfully')
        except subprocess.CalledProcessError as e:
            self.logger.error(f'Logical backup failed: {str(e)}')
    
    def verify_backups(self):
        try:
            subprocess.run(['/scripts/verify_backups.sh'], check=True)
            self.logger.info('Backup verification completed')
        except subprocess.CalledProcessError as e:
            self.logger.error(f'Backup verification failed: {str(e)}')
    
    def cleanup_old_backups(self):
        try:
            subprocess.run(['/scripts/cleanup_backups.sh'], check=True)
            self.logger.info('Backup cleanup completed')
        except subprocess.CalledProcessError as e:
            self.logger.error(f'Backup cleanup failed: {str(e)}')

# Schedule backups
backup_manager = PostgreSQLBackupManager()

# Physical backups daily at 1 AM
schedule.every().day.at("01:00").do(backup_manager.perform_physical_backup)

# Logical backups daily at 2 AM
schedule.every().day.at("02:00").do(backup_manager.perform_logical_backup)

# Verify backups daily at 3 AM
schedule.every().day.at("03:00").do(backup_manager.verify_backups)

# Cleanup old backups weekly on Sunday at 4 AM
schedule.every().sunday.at("04:00").do(backup_manager.cleanup_old_backups)

while True:
    schedule.run_pending()
    time.sleep(60)

Backup Verification

Verification Script

bash
#!/bin/bash
# verify_backups.sh

# Configuration
BACKUP_DIR="/backup/postgresql"
TEST_DIR="/tmp/postgresql_test"
LOG_FILE="/var/log/postgresql/backup_verification.log"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> ${LOG_FILE}
}

verify_physical_backup() {
    local backup_file=$1
    
    # Create test directory
    mkdir -p ${TEST_DIR}
    
    # Extract backup
    tar xf ${backup_file} -C ${TEST_DIR}
    
    # Test PostgreSQL start
    pg_ctl -D ${TEST_DIR} start -w -t 60
    if [ $? -eq 0 ]; then
        log "Physical backup verification successful: ${backup_file}"
        pg_ctl -D ${TEST_DIR} stop
    else
        log "ERROR: Physical backup verification failed: ${backup_file}"
    fi
    
    # Cleanup
    rm -rf ${TEST_DIR}
}

verify_logical_backup() {
    local backup_file=$1
    local test_db="verify_$(date +%s)"
    
    # Create test database
    createdb ${test_db}
    
    # Restore backup
    pg_restore -d ${test_db} ${backup_file}
    if [ $? -eq 0 ]; then
        log "Logical backup verification successful: ${backup_file}"
    else
        log "ERROR: Logical backup verification failed: ${backup_file}"
    fi
    
    # Cleanup
    dropdb ${test_db}
}

# Verify recent backups
for backup in ${BACKUP_DIR}/base_*.tar.gz; do
    verify_physical_backup ${backup}
done

for backup in ${BACKUP_DIR}/*.custom; do
    verify_logical_backup ${backup}
done

Best Practices

  1. Backup Strategy

    • Implement both physical and logical backups
    • Use continuous archiving for PITR
    • Regular backup verification
    • Automated backup rotation
  2. Security

    • Encrypt sensitive backups
    • Secure backup storage
    • Regular permission audits
    • Backup access logging
  3. Monitoring

    • Monitor backup success/failure
    • Track backup size trends
    • Alert on verification failures
    • Regular recovery testing
  4. Documentation

    • Detailed recovery procedures
    • Backup retention policies
    • Verification processes
    • Emergency contacts

Further Reading

Remember to regularly test your backup and recovery procedures to ensure they work when needed. A backup is only as good as your ability to restore from it.

Written by

TheDBAdmin Team

KEEP LEARNING WITH THEDBADMIN

Turn this guide into a skill you can prove

Keep reading

Featured course · New batch

NextGen DBA + AI Engineer Bootcamp

Instructor-led, lab-first training for production DBAs: PostgreSQL, MongoDB, Docker, Kubernetes and AI.

  • Live labs on real execution plans
  • Backup, PITR & HA in production
  • pgvector, RAG & AI for DBAs

🛒 FROM THE SHOP

PostgreSQL DBA with Docker is a hands-on student book

PDF · 35 pages

₹99₹5,00098% off

View product