#!/bin/bash # Daily PostgreSQL backup to R2 (14-day retention) # Schedule: cron daily. Requires: pg_dump, aws CLI or rclone for R2. set -e BACKUP_DIR="${BACKUP_DIR:-/tmp/linearvoid-backups}" RETENTION_DAYS="${RETENTION_DAYS:-14}" R2_BUCKET="${R2_BUCKET:-linearvoid-backups}" DB_URL="${DATABASE_URL:-postgres://linearvoid:linearvoid@db:5432/linearvoid?sslmode=disable}" TIMESTAMP=$(date +%Y%m%d) BACKUP_FILE="$BACKUP_DIR/daily/db_${TIMESTAMP}.dump" mkdir -p "$(dirname "$BACKUP_FILE")" echo "[$(date -Iseconds)] Starting daily backup..." # Extract connection details from DATABASE_URL DB_HOST=$(echo "$DB_URL" | sed -n 's/.*@\([^:]*\).*/\1/p') DB_PORT=$(echo "$DB_URL" | sed -n 's/.*:\([0-9]*\)\/.*/\1/p') DB_USER=$(echo "$DB_URL" | sed -n 's/.*:\/\/\([^:]*\).*/\1/p') DB_NAME=$(echo "$DB_URL" | sed -n 's/.*\/\([^?]*\).*/\1/p') DB_PASS=$(echo "$DB_URL" | sed -n 's/.*:\/\/[^:]*:\([^@]*\).*/\1/p') export PGPASSWORD="${DB_PASS}" # Dump pg_dump -h "${DB_HOST:-db}" -p "${DB_PORT:-5432}" -U "${DB_USER:-linearvoid}" \ -Fc -f "$BACKUP_FILE" "${DB_NAME:-linearvoid}" echo " Backup: $BACKUP_FILE ($(du -h "$BACKUP_FILE" | cut -f1))" # Upload to R2 if [ -n "$R2_ENDPOINT" ] && [ -n "$R2_ACCESS_KEY_ID" ]; then aws s3 cp "$BACKUP_FILE" "s3://${R2_BUCKET}/daily/db_${TIMESTAMP}.dump" \ --endpoint-url "$R2_ENDPOINT" --no-verify-ssl 2>/dev/null && \ echo " Uploaded to R2: daily/db_${TIMESTAMP}.dump" || \ echo " WARNING: R2 upload failed" fi # Cleanup old backups find "$BACKUP_DIR/daily" -name "db_*.dump" -mtime "+${RETENTION_DAYS}" -delete 2>/dev/null || true echo " Cleaned up backups older than ${RETENTION_DAYS} days" echo "[$(date -Iseconds)] Daily backup complete."