debate/scripts/backup-postgres.sh
Mikkel Georgsen 09f89617e7 feat(01-04): create PostgreSQL backup script with 30-day retention
- Add backup-postgres.sh with pg_dump custom format (-Fc)
- Verify backup integrity via pg_restore --list
- Compress backups with gzip for storage efficiency
- Delete backups older than 30 days (configurable via RETENTION_DAYS)
- Weekly restore test on Mondays to validate backup usability
- Add cron configuration for daily 2 AM backups
- Add .gitignore for pycache, env files, and backup files
2026-01-25 20:19:17 +00:00

83 lines
2.4 KiB
Bash
Executable file

#!/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"