#!/usr/bin/env bash # Nexus — MySQL Auto-Backup Script # Usage: Add to crontab for daily 3AM backup # 0 3 * * * /path/to/nexus/deploy/db_backup.sh >> /var/log/nexus_backup.log 2>&1 # # Reads DATABASE_URL from .env for connection parameters. # Retains backups for 30 days, auto-deletes older ones. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" ENV_FILE="$PROJECT_DIR/.env" BACKUP_DIR="$PROJECT_DIR/backups" RETENTION_DAYS=30 # ── Load .env ── if [ ! -f "$ENV_FILE" ]; then echo "[$(date)] ERROR: .env not found at $ENV_FILE" exit 1 fi # Parse DATABASE_URL from .env # Format: mysql+asyncmy://user:password@host:port/dbname DB_URL=$(grep -E '^NEXUS_DATABASE_URL=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'") if [ -z "$DB_URL" ]; then DB_URL=$(grep -E '^DATABASE_URL=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'") fi if [ -z "$DB_URL" ]; then echo "[$(date)] ERROR: DATABASE_URL not found in .env" exit 1 fi # Parse mysql URL: mysql+asyncmy://user:pass@host:port/dbname → user pass host port dbname # Strip scheme CONN="${DB_URL#*://}" # Extract user:pass USERPASS="${CONN%%@*}" DB_USER="${USERPASS%%:*}" DB_PASS="${USERPASS#*:}" # Extract host:port/dbname HOSTDB="${CONN#*@}" DB_HOST="${HOSTDB%%:*}" PORTDB="${HOSTDB#*:}" DB_PORT="${PORTDB%%/*}" DB_NAME="${PORTDB#*/}" # Remove query params DB_NAME="${DB_NAME%%\?*}" if [ -z "$DB_NAME" ] || [ -z "$DB_HOST" ] || [ -z "$DB_USER" ]; then echo "[$(date)] ERROR: Failed to parse DATABASE_URL" exit 1 fi # ── Create backup directory ── mkdir -p "$BACKUP_DIR" # ── Backup filename ── TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/nexus_${DB_NAME}_${TIMESTAMP}.sql.gz" # ── Run mysqldump ── echo "[$(date)] Starting backup: $DB_NAME @ $DB_HOST" if [ -n "$DB_PASS" ]; then MYSQL_PWD="$DB_PASS" mysqldump \ -h "$DB_HOST" \ -P "${DB_PORT:-3306}" \ -u "$DB_USER" \ --single-transaction \ --routines \ --triggers \ --set-gtid-purged=OFF \ "$DB_NAME" | gzip > "$BACKUP_FILE" else mysqldump \ -h "$DB_HOST" \ -P "${DB_PORT:-3306}" \ -u "$DB_USER" \ --single-transaction \ --routines \ --triggers \ --set-gtid-purged=OFF \ "$DB_NAME" | gzip > "$BACKUP_FILE" fi # ── Verify backup ── if [ -f "$BACKUP_FILE" ]; then SIZE=$(du -h "$BACKUP_FILE" | cut -f1) echo "[$(date)] Backup completed: $BACKUP_FILE ($SIZE)" else echo "[$(date)] ERROR: Backup file not created!" exit 1 fi # ── Cleanup old backups (retain 30 days) ── DELETED=$(find "$BACKUP_DIR" -name "nexus_*.sql.gz" -mtime +${RETENTION_DAYS} -delete -print | wc -l) if [ "$DELETED" -gt 0 ]; then echo "[$(date)] Cleaned up $DELETED old backup(s) (>${RETENTION_DAYS} days)" fi echo "[$(date)] Backup process complete"