#!/bin/sh # QueueMed — MySQL backup helper. # # Dumps the configured MySQL database into /app/data/backups (or $BACKUP_DIR) # with a timestamped filename, then prunes everything but the 7 most recent # backups. Designed to run inside the `app` container — schedule from the host # with `docker compose exec app /app/scripts/backup-db.sh` (cron, systemd, …). # # Required environment variables (already wired through docker-compose.yml): # MYSQL_HOST – hostname of the MySQL service (default: db) # MYSQL_DATABASE – database name to dump (default: queuemed) # MYSQL_USER – MySQL user (default: queuemed) # MYSQL_PASSWORD – MySQL password (required) # Optional: # BACKUP_DIR – override backup destination (default: /app/data/backups) # BACKUP_KEEP – number of backups to retain (default: 7) set -eu MYSQL_HOST="${MYSQL_HOST:-db}" MYSQL_DATABASE="${MYSQL_DATABASE:-queuemed}" MYSQL_USER="${MYSQL_USER:-queuemed}" BACKUP_DIR="${BACKUP_DIR:-/app/data/backups}" BACKUP_KEEP="${BACKUP_KEEP:-7}" if [ -z "${MYSQL_PASSWORD:-}" ]; then echo "[backup-db] MYSQL_PASSWORD is not set, aborting." >&2 exit 1 fi mkdir -p "$BACKUP_DIR" TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)" OUT_FILE="$BACKUP_DIR/${MYSQL_DATABASE}-${TIMESTAMP}.sql.gz" TMP_FILE="${OUT_FILE}.partial" echo "[backup-db] dumping ${MYSQL_DATABASE} from ${MYSQL_HOST} -> ${OUT_FILE}" # --single-transaction keeps the dump consistent without locking InnoDB tables. # --quick streams rows row-by-row to keep memory bounded for large tables. mysqldump \ --host="$MYSQL_HOST" \ --user="$MYSQL_USER" \ --password="$MYSQL_PASSWORD" \ --single-transaction \ --quick \ --routines \ --triggers \ --no-tablespaces \ --default-character-set=utf8mb4 \ "$MYSQL_DATABASE" | gzip -9 > "$TMP_FILE" mv "$TMP_FILE" "$OUT_FILE" echo "[backup-db] backup written: $OUT_FILE ($(wc -c < "$OUT_FILE") bytes)" # ── Rotate: keep only the last $BACKUP_KEEP backups ───────────────────────── # `ls -1t` sorts by mtime descending; everything after the first $BACKUP_KEEP # entries is removed. Filenames are constrained to our prefix to avoid eating # unrelated files that might share the directory. cd "$BACKUP_DIR" ls -1t "${MYSQL_DATABASE}"-*.sql.gz 2>/dev/null \ | awk -v keep="$BACKUP_KEEP" 'NR > keep' \ | while IFS= read -r old; do echo "[backup-db] pruning old backup: $old" rm -f -- "$old" done echo "[backup-db] done."