#!/bin/bash # PostgreSQL backup script for Debate platform # Runs daily, keeps 30 days of backups # Verifies backup integrity after creation set -euo pipefail # Configuration BACKUP_DIR="${BACKUP_DIR:-/var/backups/debate/postgres}" RETENTION_DAYS="${RETENTION_DAYS:-30}" CONTAINER_NAME="${CONTAINER_NAME:-debate-postgres}" DB_NAME="${DB_NAME:-debate}" DB_USER="${DB_USER:-debate}" TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.dump" # Logging log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" } # Create backup directory mkdir -p "$BACKUP_DIR" log "Starting backup of database: $DB_NAME" # Create backup using pg_dump custom format (-Fc) # Custom format is compressed and allows selective restore docker exec "$CONTAINER_NAME" pg_dump \ -U "$DB_USER" \ -Fc \ -b \ -v \ "$DB_NAME" > "$BACKUP_FILE" 2>/dev/null log "Backup created: $BACKUP_FILE" # Verify backup integrity using pg_restore --list # This reads the archive table of contents without restoring # We pipe the backup into the container since pg_restore is only available there log "Verifying backup integrity..." cat "$BACKUP_FILE" | docker exec -i "$CONTAINER_NAME" pg_restore --list > /dev/null 2>&1 || { log "ERROR: Backup verification failed!" rm -f "$BACKUP_FILE" exit 1 } # Get backup size BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) log "Backup size: $BACKUP_SIZE" # Compress backup (pg_dump -Fc includes compression, but gzip adds more) gzip -f "$BACKUP_FILE" log "Compressed: ${BACKUP_FILE}.gz" # Clean up old backups log "Removing backups older than $RETENTION_DAYS days..." find "$BACKUP_DIR" -name "${DB_NAME}_*.dump.gz" -mtime +$RETENTION_DAYS -delete REMAINING=$(find "$BACKUP_DIR" -name "${DB_NAME}_*.dump.gz" | wc -l) log "Remaining backups: $REMAINING" # Weekly restore test (every Monday) if [ "$(date +%u)" -eq 1 ]; then log "Running weekly restore test..." TEST_DB="${DB_NAME}_backup_test" # Create test database docker exec "$CONTAINER_NAME" createdb -U "$DB_USER" "$TEST_DB" 2>/dev/null || true # Restore to test database gunzip -c "${BACKUP_FILE}.gz" | docker exec -i "$CONTAINER_NAME" pg_restore \ -U "$DB_USER" \ -d "$TEST_DB" \ --clean \ --if-exists 2>&1 || true # Drop test database docker exec "$CONTAINER_NAME" dropdb -U "$DB_USER" "$TEST_DB" 2>/dev/null || true log "Weekly restore test completed" fi log "Backup completed successfully"