#!/bin/sh set -e BACKUP_DIR="${BACKUP_DIR:-/backups}" DB_HOST="${POSTGRES_HOST:-db}" DB_NAME="${POSTGRES_DB:-bollwerk}" DB_USER="${POSTGRES_USER:-bollwerk}" MAX_SIZE_BYTES=$((1 * 1024 * 1024 * 1024)) # 1 GB mkdir -p "$BACKUP_DIR" TIMESTAMP=$(date -u +"%Y-%m-%d_%H%M") BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.sql.gz" CHECKSUM_FILE="${BACKUP_DIR}/.last_checksum" echo "[$(date -u)] Starting backup of database '${DB_NAME}' on host '${DB_HOST}'..." # Dump to temp file for checksum comparison TEMP_DUMP=$(mktemp) export PGPASSWORD="${POSTGRES_PASSWORD:-}" pg_dump -h "$DB_HOST" -U "$DB_USER" "$DB_NAME" > "$TEMP_DUMP" # Compute MD5 of dump content, excluding lines that change every run: # - pg_dump comment lines (timestamps, version info) # - PostgreSQL 17 security tokens (\restrict / \unrestrict with random token) CURRENT_CHECKSUM=$(grep -Ev '^(--|\\restrict|\\unrestrict)' "$TEMP_DUMP" | md5sum | cut -d' ' -f1) LAST_CHECKSUM="" if [ -f "$CHECKSUM_FILE" ]; then LAST_CHECKSUM=$(cat "$CHECKSUM_FILE") fi if [ "$CURRENT_CHECKSUM" = "$LAST_CHECKSUM" ]; then echo "[$(date -u)] No changes since last backup. Skipping." rm -f "$TEMP_DUMP" exit 0 fi # Compress and save gzip -c "$TEMP_DUMP" > "$BACKUP_FILE" rm -f "$TEMP_DUMP" printf '%s' "$CURRENT_CHECKSUM" > "$CHECKSUM_FILE" echo "[$(date -u)] Backup saved: ${BACKUP_FILE}" # Rotate: remove oldest .sql.gz until total backup dir size <= 1 GB while true; do TOTAL=$(du -sb "$BACKUP_DIR" | cut -f1) if [ "$TOTAL" -le "$MAX_SIZE_BYTES" ]; then break fi OLDEST=$(ls -tr "${BACKUP_DIR}"/*.sql.gz 2>/dev/null | head -1) if [ -z "$OLDEST" ]; then echo "[$(date -u)] Warning: Cannot reduce backup dir below 1 GB, no more files to delete." break fi echo "[$(date -u)] Rotation: removing ${OLDEST} (dir total: ${TOTAL} bytes)" rm -f "$OLDEST" done echo "[$(date -u)] Backup complete."