Script 07 · Database Automation

7. Primary DB Auto Config & Backup

Initializes a primary PostgreSQL data directory, tunes database settings, and provides backup/restore migration flow.

Category: Database Automation Risk: Destructive Lines: calculating Language: Bash / Linux
Back to index

What this script does

  • Create a dedicated primary DB cluster under /home/postgres.
  • Tune PostgreSQL for Erupe workloads.
  • Move data from a source DB to a target DB.

Prerequisites

  • Root access
  • PostgreSQL installed
  • Known source/target credentials
  • Reliable backup storage

Execution flow

  1. Detects PostgreSQL version
  2. Frees selected port
  3. Recreates primary data directory
  4. Writes postgresql.conf and pg_hba.conf
  5. Runs backup/restore

Validation checklist

  • pg_isready -p <port>
  • psql connection test
  • pg_dump/pg_restore exit codes
  • system logs

Operational cautions

  • Deletes /home/postgres/primary_db if present.
  • trust authentication is unsafe for public networks.
  • Killing a port can stop unrelated services.

Original script notes

ℹ️ Script Info: Initializes the primary PostgreSQL database cluster, clears conflicting ports, applies advanced database performance tweaks, and includes built-in backup and restore functions.

Script source
cat << 'EOF' > setup_postgres_final.sh
#!/bin/bash

# ==========================================
# PostgreSQL Final Automation Script
# Auto Kill Port, Trust Auth, Full Backup
# ==========================================

# Check Root
if [ "$EUID" -ne 0 ]; then
  echo "Run as root (sudo)."
  exit 1
fi

# Error Handling Function
handle_error() {
    echo "[ERROR] Occurred at stage: $1"
    apt --fix-broken install -y
    dpkg --configure -a
}

echo "=== 1. SYSTEM & USER PREP ==="
# Install extra tools for kill port (fuser) and locate
apt update
apt install plocate psmisc -y

# User Postgres
if id "postgres" >/dev/null; then
    echo "User 'postgres' already exists."
else
    echo "Creating user 'postgres'..."
    useradd -m -s /bin/bash postgres
    echo "Set password for Linux user 'postgres':"
    passwd postgres
fi

echo -e "\n=== 2. DETECTION & POSTGRES PATH ==="
PG_VERSION=$(ls /usr/lib/postgresql/ | sort -V | tail -n 1)
if [ -z "$PG_VERSION" ]; then
    echo "Installing PostgreSQL..."
    apt install postgresql postgresql-contrib -y
    PG_VERSION=$(ls /usr/lib/postgresql/ | sort -V | tail -n 1)
fi

PG_BIN_PATH="/usr/lib/postgresql/$PG_VERSION/bin"
echo "PostgreSQL Version: $PG_VERSION"

# Setup .bashrc
setup_path() {
    local target_home=$2
    if ! grep -q "$PG_BIN_PATH" $target_home/.bashrc; then
        echo "export PATH=\$PATH:$PG_BIN_PATH" >> $target_home/.bashrc
    fi
}
setup_path "postgres" "/home/postgres"
setup_path "root" "/root"
export PATH=$PATH:$PG_BIN_PATH

echo -e "\n=== 3. DATABASE INITIALIZATION ==="
echo "Define Port for Primary DB (This port will be cleared if currently in use)."
read -p "Enter PORT: " PG_PORT

# CLEANUP & KILL PORT LOGIC
echo ">> Checking availability of port and old directories..."

# 1. Check Directory
if [ -d "/home/postgres/primary_db" ]; then
    echo "Old directory found. Attempting to stop related service..."
    # Try graceful stop
    sudo -u postgres $PG_BIN_PATH/pg_ctl -D /home/postgres/primary_db stop -m fast 2>/dev/null
fi

# 2. Kill Port Forcefully (Safety measure)
if fuser -n tcp $PG_PORT 2>/dev/null; then
    echo "Port $PG_PORT is in use. Killing process forcefully..."
    fuser -k -n tcp $PG_PORT
    sleep 2
    echo "Port $PG_PORT successfully freed."
fi

# 3. Delete Directory
if [ -d "/home/postgres/primary_db" ]; then
    echo "Removing old directory /home/postgres/primary_db ..."
    rm -rf /home/postgres/primary_db
fi

# Create New Directory
mkdir -p /home/postgres/primary_db
chown -R postgres:postgres /home/postgres
chmod -R 700 /home/postgres

echo ">> Performing InitDB..."
sudo -u postgres $PG_BIN_PATH/initdb -D /home/postgres/primary_db

CONF_FILE="/home/postgres/primary_db/postgresql.conf"
HBA_FILE="/home/postgres/primary_db/pg_hba.conf"

echo ">> Configuring postgresql.conf..."
sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/" $CONF_FILE
sed -i "s/#port = 5432/port = $PG_PORT/" $CONF_FILE

echo -e "\n=== INJECT CUSTOM CONFIGURATION ==="
echo "Select PostgreSQL Performance Profile:"
echo "1) Default Tweak Minimum (Recommended for smaller servers)"
echo "2) Advanced Tweak (High Performance / High RAM)"
read -p "Choice (1/2): " TWEAK_OPT

if [ "$TWEAK_OPT" == "2" ]; then
    CONFIG_LIST="max_connections = 200
shared_buffers = 1024MB
effective_cache_size = 3072MB
maintenance_work_mem = 256MB
checkpoint_completion_target = 0.9
wal_buffers = 32MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 32MB
huge_pages = off
min_wal_size = 512MB
max_wal_size = 4GB
wal_writer_delay = 200ms
synchronous_commit = off
max_worker_processes = 8
max_parallel_maintenance_workers = 2
tcp_keepalives_idle = 60
tcp_keepalives_interval = 5
tcp_keepalives_count = 6
tcp_user_timeout = 0
client_connection_check_interval = 5000
dynamic_shared_memory_type = posix
autovacuum = on
autovacuum_vacuum_scale_factor = 0.1
autovacuum_analyze_scale_factor = 0.05
autovacuum_vacuum_cost_limit = 200
jit = off"
    echo "Selected Advanced Tweak."
else
    CONFIG_LIST="max_connections = 250
shared_buffers = 512MB
effective_cache_size = 1024MB
maintenance_work_mem = 64MB
checkpoint_completion_target = 0.9
wal_buffers = 4MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 250
work_mem = 4MB
huge_pages = off
min_wal_size = 128MB
max_wal_size = 1GB
wal_keep_size = 256MB
wal_level = replica
synchronous_commit = on
max_replication_slots = 10
max_wal_senders = 10
max_worker_processes = 2
max_parallel_workers_per_gather = 4
max_parallel_workers = 2
max_parallel_maintenance_workers = 4
tcp_keepalives_idle = 60
tcp_keepalives_interval = 5
tcp_keepalives_count = 4
tcp_user_timeout = 0
client_connection_check_interval = 0
dynamic_shared_memory_type = posix
autovacuum = on"
    echo "Selected Default Tweak Minimum."
fi

echo "$CONFIG_LIST" | while IFS= read -r line; do
    if [ ! -z "$line" ]; then
        KEY=$(echo "$line" | cut -d'=' -f1 | sed 's/ //g')
        sed -i "s/^$KEY\s*=/ #$KEY =/" $CONF_FILE
        sed -i "s/^#$KEY\s*=/ #$KEY =/" $CONF_FILE
        echo "$line" >> $CONF_FILE
    fi
done

echo ">> Configuring pg_hba.conf (TRUST Method)..."
# Override for TRUST 0.0.0.0/0
echo "host    all             all             0.0.0.0/0               trust" >> $HBA_FILE

echo ">> Starting Database..."
sudo -u postgres $PG_BIN_PATH/pg_ctl -D /home/postgres/primary_db start
sleep 3

echo -e "\n=== 4. CREATE DATABASE & DB USER ==="
read -s -p "Enter password for DB user 'postgres' (Optional due to Trust, but recommended): " DB_PASS
echo ""

sudo -u postgres $PG_BIN_PATH/psql -p $PG_PORT -d postgres <<QueryEOF
ALTER USER postgres WITH ENCRYPTED PASSWORD '$DB_PASS';
CREATE DATABASE erupe;
GRANT ALL PRIVILEGES ON DATABASE erupe TO postgres;
QueryEOF

sudo -u postgres $PG_BIN_PATH/pg_ctl -D /home/postgres/primary_db restart
sleep 2

echo -e "\n=== 5. BACKUP PROCESS (FULL CONTENT) ==="
while true; do
    echo ">> SETUP SOURCE SERVER:"
    read -p "Source IP Address: " SRC_IP
    read -p "Source Port: " SRC_PORT
    read -p "Source DB User: " SRC_USER
    read -s -p "Source DB Password: " SRC_PASS
    echo ""
    read -p "Name of Source Database to backup: " SRC_DB_NAME

    # Check Connection
    echo "Attempting connection to source..."
    PGPASSWORD=$SRC_PASS $PG_BIN_PATH/psql -h $SRC_IP -p $SRC_PORT -U $SRC_USER -d $SRC_DB_NAME -c '\q' 2>/dev/null
    
    if [ $? -eq 0 ]; then
        echo "Connection Successful."
        read -p "Backup Directory (e.g., /root/backup): " BACKUP_DIR
        mkdir -p "$BACKUP_DIR"
        read -p "Backup Filename (without extension): " BACKUP_NAME
        FULL_PATH="$BACKUP_DIR/$BACKUP_NAME.sql"
        
        echo ">> Running Full Backup (Schema + Data + Blobs)..."
        # pg_dump -Fc includes blobs (-b) by default, but we flag it to be sure.
        # No filter --schema-only or --data-only, so "everything" for that DB.
        PGPASSWORD="$SRC_PASS" $PG_BIN_PATH/pg_dump -h "$SRC_IP" -p "$SRC_PORT" -U "$SRC_USER" -Fc -b -v -f "$FULL_PATH" -d "$SRC_DB_NAME"
        
        if [ $? -eq 0 ]; then
            echo "Backup Complete: $FULL_PATH"
            BACKUP_SUCCESS=true
            break
        else
            echo "Backup Failed during execution."
            read -p "Try again? (y/n): " RETRY
            if [[ "$RETRY" != "y" ]]; then BACKUP_SUCCESS=false; break; fi
        fi
    else
        echo "Connection failed / Auth Incorrect."
        read -p "Re-input data? (y/n): " RETRY
        if [[ "$RETRY" != "y" ]]; then BACKUP_SUCCESS=false; break; fi
    fi
done

echo -e "\n=== 6. RESTORE PROCESS ==="
if [ "$BACKUP_SUCCESS" = true ]; then
    while true; do
        echo ">> SETUP TARGET SERVER:"
        read -p "Target IP Address: " TGT_IP
        read -p "Target Port: " TGT_PORT
        read -p "Target DB User: " TGT_USER
        read -s -p "Target DB Password: " TGT_PASS
        echo ""
        read -p "Target Database Name for Restore: " TGT_DB_NAME

        # Check Existing Database
        DB_CHECK=$(PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$TGT_DB_NAME'" 2>/dev/null)
        
        FINAL_DB_NAME="$TGT_DB_NAME"
        DO_RESTORE=false

        if [ "$DB_CHECK" == "1" ]; then
            echo "Database '$TGT_DB_NAME' ALREADY EXISTS."
            echo "1. Delete old DB & Re-Restore (Overwrite)."
            echo "2. Restore to New DB (Rename)."
            read -p "Choice (1/2): " OPT
            
            if [ "$OPT" == "1" ]; then
                echo "Terminating connections & Deleting old DB..."
                PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$TGT_DB_NAME';" >/dev/null
                PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "DROP DATABASE \"$TGT_DB_NAME\";"
                
                echo "Recreating DB..."
                PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "CREATE DATABASE \"$TGT_DB_NAME\";"
                PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "ALTER DATABASE \"$TGT_DB_NAME\" OWNER TO postgres;"
                DO_RESTORE=true
            elif [ "$OPT" == "2" ]; then
                read -p "NEW Database Name: " NEW_DB_NAME
                FINAL_DB_NAME="$NEW_DB_NAME"
                echo "Creating DB '$FINAL_DB_NAME'..."
                PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "CREATE DATABASE \"$FINAL_DB_NAME\";"
                if [ $? -eq 0 ]; then
                      PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "ALTER DATABASE \"$FINAL_DB_NAME\" OWNER TO postgres;"
                      DO_RESTORE=true
                else
                      echo "Failed to create new DB."
                fi
            fi
        else
            echo "Database does not exist. Creating new..."
            PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "CREATE DATABASE \"$TGT_DB_NAME\";"
            if [ $? -eq 0 ]; then
                PGPASSWORD=$TGT_PASS $PG_BIN_PATH/psql -h $TGT_IP -p $TGT_PORT -U $TGT_USER -d postgres -c "ALTER DATABASE \"$TGT_DB_NAME\" OWNER TO postgres;"
                DO_RESTORE=true
            fi
        fi

        if [ "$DO_RESTORE" = true ]; then
            echo ">> Starting Restore to '$FINAL_DB_NAME'..."
            # Restore -v (verbose), -d (database)
            PGPASSWORD="$TGT_PASS" $PG_BIN_PATH/pg_restore -h "$TGT_IP" -p "$TGT_PORT" -U "$TGT_USER" -d "$FINAL_DB_NAME" -v "$FULL_PATH"
            
            if [ $? -eq 0 ]; then
                echo "RESTORE SUCCESS!"
                break
            else
                echo "Restore finished with Warning/Error."
                read -p "Retry Restore? (y/n): " RETRY
                if [[ "$RETRY" != "y" ]]; then break; fi
            fi
        else
            read -p "Failed setup target DB. Retry input? (y/n): " RETRY
            if [[ "$RETRY" != "y" ]]; then break; fi
        fi
    done
fi

echo "========================================="
echo "DONE. Script saved at: $(pwd)/setup_postgres_final.sh"
echo "========================================="
EOF

chmod +x setup_postgres_final.sh && ./setup_postgres_final.sh
Done