Skip to content

Instantly share code, notes, and snippets.

@phaneesh
Last active April 15, 2025 14:58
Show Gist options
  • Save phaneesh/703f49b36f721b8da007f123c06c89fa to your computer and use it in GitHub Desktop.
Save phaneesh/703f49b36f721b8da007f123c06c89fa to your computer and use it in GitHub Desktop.
PostgreSql Daily Backup & Restore
#!/bin/bash
#
# PostgreSQL Daily Backup Script
# This script creates daily backups of PostgreSQL databases with rotation
#
# Configuration variables
BACKUP_DIR="/var/backups/postgresql"
DB_USER="postgres"
DB_PASSWORD="" # For security, consider using a .pgpass file instead
DB_HOST="localhost"
DB_PORT="5432"
DAYS_TO_KEEP=7
DATE_FORMAT=$(date +"%Y-%m-%d")
LOG_FILE="${BACKUP_DIR}/backup_log_${DATE_FORMAT}.log"
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Initialize log file
echo "===== PostgreSQL Backup Started at $(date) =====" > "$LOG_FILE"
# Function to log messages
log_message() {
echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" >> "$LOG_FILE"
echo "$1"
}
# Function to handle errors
handle_error() {
log_message "ERROR: $1"
exit 1
}
# Check if pg_dump is available
if ! command -v pg_dump &> /dev/null; then
handle_error "pg_dump command not found. Please install PostgreSQL client tools."
fi
# Backup all databases
backup_all_databases() {
log_message "Starting backup of all databases..."
# Get list of databases, excluding template databases
DB_LIST=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -t -c "SELECT datname FROM pg_database WHERE datname NOT IN ('template0', 'template1') AND datname NOT LIKE 'pg_%';" 2>> "$LOG_FILE")
if [ $? -ne 0 ]; then
handle_error "Failed to get database list"
fi
# Backup each database
for DB_NAME in $DB_LIST; do
backup_single_database "$DB_NAME"
done
log_message "All database backups completed!"
}
# Backup a single database
backup_single_database() {
DB_NAME="$1"
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE_FORMAT}.sql.gz"
log_message "Backing up database: $DB_NAME to $BACKUP_FILE"
PGPASSWORD="$DB_PASSWORD" pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" \
-F p -b -v -f - "$DB_NAME" 2>> "$LOG_FILE" | gzip > "$BACKUP_FILE"
if [ $? -ne 0 ]; then
log_message "Warning: There might have been a problem with the backup of $DB_NAME. Check the log for details."
else
log_message "Successfully backed up database: $DB_NAME"
# Set appropriate permissions for backup file
chmod 600 "$BACKUP_FILE"
fi
}
# Delete old backups
rotate_backups() {
log_message "Removing backups older than $DAYS_TO_KEEP days..."
find "$BACKUP_DIR" -name "*.sql.gz" -type f -mtime +$DAYS_TO_KEEP -delete
find "$BACKUP_DIR" -name "backup_log_*.log" -type f -mtime +$DAYS_TO_KEEP -delete
log_message "Old backups removed!"
}
# Main execution
log_message "Starting PostgreSQL backup process..."
# Execute backups
backup_all_databases
# Rotate old backups
rotate_backups
log_message "Backup process completed successfully!"
echo "===== PostgreSQL Backup Completed at $(date) =====" >> "$LOG_FILE"
exit 0
#!/bin/bash
#
# PostgreSQL Backup Restore Script
# This script restores PostgreSQL databases from backups created by the backup script
#
# Configuration variables
BACKUP_DIR="/var/backups/postgresql"
DB_USER="postgres"
DB_PASSWORD="" # For security, consider using a .pgpass file instead
DB_HOST="localhost"
DB_PORT="5432"
LOG_FILE="${BACKUP_DIR}/restore_log_$(date +"%Y-%m-%d_%H-%M-%S").log"
# Create log directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Initialize log file
echo "===== PostgreSQL Restore Started at $(date) =====" > "$LOG_FILE"
# Function to log messages
log_message() {
echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" >> "$LOG_FILE"
echo "$1"
}
# Function to handle errors
handle_error() {
log_message "ERROR: $1"
exit 1
}
# Check if psql is available
if ! command -v psql &> /dev/null; then
handle_error "psql command not found. Please install PostgreSQL client tools."
fi
# Display available backups
list_backups() {
echo "Available database backups:"
ls -lt "$BACKUP_DIR" | grep ".sql.gz" | awk '{print NR". "$9}'
}
# Restore a database from backup
restore_database() {
BACKUP_FILE="$1"
DB_NAME="$2"
# Check if backup file exists
if [ ! -f "$BACKUP_FILE" ]; then
handle_error "Backup file does not exist: $BACKUP_FILE"
fi
# Check if database exists
DB_EXISTS=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -t -c "SELECT 1 FROM pg_database WHERE datname='$DB_NAME';" 2>> "$LOG_FILE")
if [ "$DB_EXISTS" = " 1" ]; then
read -p "Database $DB_NAME already exists. Do you want to drop and recreate it? (y/n): " CONFIRM
if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
log_message "Dropping existing database: $DB_NAME"
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -c "DROP DATABASE \"$DB_NAME\";" 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
handle_error "Failed to drop database $DB_NAME"
fi
else
log_message "Restore cancelled by user."
exit 0
fi
fi
# Create database
log_message "Creating database: $DB_NAME"
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -c "CREATE DATABASE \"$DB_NAME\";" 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
handle_error "Failed to create database $DB_NAME"
fi
# Restore from backup
log_message "Restoring $DB_NAME from $BACKUP_FILE..."
gunzip -c "$BACKUP_FILE" | PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
log_message "Warning: There might have been a problem with the restore of $DB_NAME. Check the log for details."
else
log_message "Successfully restored database: $DB_NAME from $BACKUP_FILE"
fi
}
# Interactive mode - let user choose which backup to restore
interactive_restore() {
# List available backups
list_backups
# Ask user to select a backup
read -p "Enter the number of the backup to restore: " BACKUP_NUM
# Get the selected backup file
BACKUP_FILE=$(ls -t "$BACKUP_DIR"/*.sql.gz | sed -n "${BACKUP_NUM}p")
if [ -z "$BACKUP_FILE" ]; then
handle_error "Invalid backup number"
fi
# Extract database name from backup file
DB_NAME=$(basename "$BACKUP_FILE" | sed 's/\(.*\)_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\.sql\.gz/\1/')
echo "Selected backup file: $BACKUP_FILE"
echo "Database name: $DB_NAME"
# Confirm restoration
read -p "Do you want to restore this database? (y/n): " CONFIRM
if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
restore_database "$BACKUP_FILE" "$DB_NAME"
else
log_message "Restore cancelled by user."
fi
}
# Specific restore - restore a specific backup file for a specific database
specific_restore() {
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: $0 -s <backup_file> <database_name>"
exit 1
fi
BACKUP_FILE="$1"
DB_NAME="$2"
# Check if backup file exists
if [ ! -f "$BACKUP_FILE" ]; then
# Try adding the backup directory
BACKUP_FILE="$BACKUP_DIR/$BACKUP_FILE"
if [ ! -f "$BACKUP_FILE" ]; then
handle_error "Backup file does not exist: $1"
fi
fi
restore_database "$BACKUP_FILE" "$DB_NAME"
}
# Latest restore - restore the latest backup of a database
latest_restore() {
if [ -z "$1" ]; then
echo "Usage: $0 -l <database_name>"
exit 1
fi
DB_NAME="$1"
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/${DB_NAME}_*.sql.gz 2>/dev/null | head -n1)
if [ -z "$LATEST_BACKUP" ]; then
handle_error "No backup found for database $DB_NAME"
fi
echo "Latest backup found: $LATEST_BACKUP"
read -p "Do you want to restore this database? (y/n): " CONFIRM
if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
restore_database "$LATEST_BACKUP" "$DB_NAME"
else
log_message "Restore cancelled by user."
fi
}
# Main execution
case "$1" in
-i|--interactive)
interactive_restore
;;
-s|--specific)
specific_restore "$2" "$3"
;;
-l|--latest)
latest_restore "$2"
;;
*)
echo "PostgreSQL Backup Restore Script"
echo "Usage:"
echo " $0 -i, --interactive : Interactive mode, select backup from a list"
echo " $0 -s, --specific <file> <db> : Restore specific backup file to database"
echo " $0 -l, --latest <db> : Restore the latest backup of a database"
exit 1
;;
esac
log_message "Restore process completed!"
echo "===== PostgreSQL Restore Completed at $(date) =====" >> "$LOG_FILE"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment