-
Notifications
You must be signed in to change notification settings - Fork 166
Add automated PostgreSQL backup service #1816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,110 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| #!/bin/bash | ||||||||||||||||||||||||||||||||||||||||||||
| set -e | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| BACKUP_DIR="/backups" | ||||||||||||||||||||||||||||||||||||||||||||
| RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}" | ||||||||||||||||||||||||||||||||||||||||||||
| SCHEDULE="${BACKUP_SCHEDULE:-0 2 * * *}" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| echo "PostgreSQL Backup Service Started" | ||||||||||||||||||||||||||||||||||||||||||||
| echo "Backup directory: $BACKUP_DIR" | ||||||||||||||||||||||||||||||||||||||||||||
| echo "Retention period: $RETENTION_DAYS days" | ||||||||||||||||||||||||||||||||||||||||||||
| echo "Backup schedule: $SCHEDULE" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Install cronie for scheduled backups | ||||||||||||||||||||||||||||||||||||||||||||
| apt-get update -qq && apt-get install -y -qq cron > /dev/null 2>&1 | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| perform_backup() { | ||||||||||||||||||||||||||||||||||||||||||||
| local timestamp=$(date +%Y%m%d_%H%M%S) | ||||||||||||||||||||||||||||||||||||||||||||
| local backup_file="${BACKUP_DIR}/backup_${timestamp}.sql.gz" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting backup to $backup_file" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Perform the backup | ||||||||||||||||||||||||||||||||||||||||||||
| PGPASSWORD="$POSTGRES_PASSWORD" pg_dump \ | ||||||||||||||||||||||||||||||||||||||||||||
| -h "$POSTGRES_HOST" \ | ||||||||||||||||||||||||||||||||||||||||||||
| -p "$POSTGRES_PORT" \ | ||||||||||||||||||||||||||||||||||||||||||||
| -U "$POSTGRES_USER" \ | ||||||||||||||||||||||||||||||||||||||||||||
| -d "$POSTGRES_DB" \ | ||||||||||||||||||||||||||||||||||||||||||||
| --verbose \ | ||||||||||||||||||||||||||||||||||||||||||||
| 2>&1 | gzip > "$backup_file" | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+29
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if [ ${PIPESTATUS[0]} -eq 0 ]; then | ||||||||||||||||||||||||||||||||||||||||||||
| local size=$(du -h "$backup_file" | cut -f1) | ||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed successfully: $backup_file ($size)" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Cleanup old backups | ||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Cleaning up backups older than $RETENTION_DAYS days" | ||||||||||||||||||||||||||||||||||||||||||||
| find "$BACKUP_DIR" -name "backup_*.sql.gz" -type f -mtime +$RETENTION_DAYS -delete | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| local remaining=$(find "$BACKUP_DIR" -name "backup_*.sql.gz" -type f | wc -l) | ||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Retained backups: $remaining" | ||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Backup failed" >&2 | ||||||||||||||||||||||||||||||||||||||||||||
| rm -f "$backup_file" | ||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Backup on all databases (including analytics) | ||||||||||||||||||||||||||||||||||||||||||||
| backup_all_databases() { | ||||||||||||||||||||||||||||||||||||||||||||
| local timestamp=$(date +%Y%m%d_%H%M%S) | ||||||||||||||||||||||||||||||||||||||||||||
| local backup_file="${BACKUP_DIR}/backup_all_${timestamp}.sql.gz" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting full cluster backup to $backup_file" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| PGPASSWORD="$POSTGRES_PASSWORD" pg_dumpall \ | ||||||||||||||||||||||||||||||||||||||||||||
| -h "$POSTGRES_HOST" \ | ||||||||||||||||||||||||||||||||||||||||||||
| -p "$POSTGRES_PORT" \ | ||||||||||||||||||||||||||||||||||||||||||||
| -U "$POSTGRES_USER" \ | ||||||||||||||||||||||||||||||||||||||||||||
| --verbose \ | ||||||||||||||||||||||||||||||||||||||||||||
| 2>&1 | gzip > "$backup_file" | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+60
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if [ ${PIPESTATUS[0]} -eq 0 ]; then | ||||||||||||||||||||||||||||||||||||||||||||
| local size=$(du -h "$backup_file" | cut -f1) | ||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Full backup completed successfully: $backup_file ($size)" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Cleanup old full backups | ||||||||||||||||||||||||||||||||||||||||||||
| find "$BACKUP_DIR" -name "backup_all_*.sql.gz" -type f -mtime +$RETENTION_DAYS -delete | ||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Full backup failed" >&2 | ||||||||||||||||||||||||||||||||||||||||||||
| rm -f "$backup_file" | ||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Wait for PostgreSQL to be ready | ||||||||||||||||||||||||||||||||||||||||||||
| echo "Waiting for PostgreSQL to be ready..." | ||||||||||||||||||||||||||||||||||||||||||||
| until PGPASSWORD="$POSTGRES_PASSWORD" pg_isready -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER"; do | ||||||||||||||||||||||||||||||||||||||||||||
| sleep 2 | ||||||||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||||||||
| echo "PostgreSQL is ready" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Create backup directory if it doesn't exist | ||||||||||||||||||||||||||||||||||||||||||||
| mkdir -p "$BACKUP_DIR" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Perform initial backup | ||||||||||||||||||||||||||||||||||||||||||||
| echo "Performing initial backup..." | ||||||||||||||||||||||||||||||||||||||||||||
| perform_backup | ||||||||||||||||||||||||||||||||||||||||||||
| backup_all_databases | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Setup cron job | ||||||||||||||||||||||||||||||||||||||||||||
| echo "$SCHEDULE root /usr/local/bin/backup-job.sh >> /var/log/backup.log 2>&1" > /etc/cron.d/postgres-backup | ||||||||||||||||||||||||||||||||||||||||||||
| chmod 0644 /etc/cron.d/postgres-backup | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Create the backup job script | ||||||||||||||||||||||||||||||||||||||||||||
| cat > /usr/local/bin/backup-job.sh << 'EOF' | ||||||||||||||||||||||||||||||||||||||||||||
| #!/bin/bash | ||||||||||||||||||||||||||||||||||||||||||||
| source /etc/environment | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+90
to
+97
|
||||||||||||||||||||||||||||||||||||||||||||
| # Setup cron job | |
| echo "$SCHEDULE root /usr/local/bin/backup-job.sh >> /var/log/backup.log 2>&1" > /etc/cron.d/postgres-backup | |
| chmod 0644 /etc/cron.d/postgres-backup | |
| # Create the backup job script | |
| cat > /usr/local/bin/backup-job.sh << 'EOF' | |
| #!/bin/bash | |
| source /etc/environment | |
| # Determine absolute path to this script so cron job can source it | |
| SCRIPT_PATH="$(readlink -f "$0")" | |
| # Setup cron job | |
| echo "$SCHEDULE root /usr/local/bin/backup-job.sh >> /var/log/backup.log 2>&1" > /etc/cron.d/postgres-backup | |
| chmod 0644 /etc/cron.d/postgres-backup | |
| # Create the backup job script | |
| cat > /usr/local/bin/backup-job.sh << EOF | |
| #!/bin/bash | |
| source /etc/environment | |
| # Source the main backup script to load perform_backup and related functions | |
| source "$SCRIPT_PATH" |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tail command attempts to follow /var/log/backup.log, but this file is never created. The cron job in line 91 redirects output to this location, but the file won't exist until the first scheduled backup runs. This will cause tail to fail if the file doesn't exist. Use tail -f /var/log/backup.log with the -F flag instead, or ensure the log file is created before tailing it.
| cron && tail -f /var/log/cron.log /var/log/backup.log | |
| cron && tail -F /var/log/cron.log /var/log/backup.log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The postgres:14.4 image is outdated and may contain known security vulnerabilities. PostgreSQL 14.4 was released in 2022. Consider using a more recent patch version within the 14.x series or upgrading to a newer major version to ensure security patches are applied.