Configure MySQL binary logging and implement automated point-in-time recovery procedures for comprehensive database backup and restore operations with monitoring.
Prerequisites
- MySQL server installed
- Root or sudo access
- At least 10GB free disk space for backups
What this solves
MySQL point-in-time recovery (PITR) allows you to restore your database to any specific moment in time by combining full backups with binary logs that record every database change. This is essential for recovering from data corruption, accidental deletions, or system failures while minimizing data loss.
Step-by-step configuration
Install MySQL server
Install MySQL server with the necessary tools for backup and binary log management.
sudo apt update
sudo apt install -y mysql-server mysql-client
Configure MySQL binary logging
Enable binary logging in MySQL configuration to capture all database changes for point-in-time recovery.
[mysqld]
Binary logging configuration
log-bin = /var/log/mysql/mysql-bin
server-id = 1
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M
sync_binlog = 1
Enable GTID for better replication consistency
gtid_mode = ON
enforce_gtid_consistency = ON
log_slave_updates = ON
InnoDB settings for durability
innodb_flush_log_at_trx_commit = 1
innodb_support_xa = 1
Create binary log directory
Create the binary log directory with proper permissions for the mysql user.
sudo mkdir -p /var/log/mysql
sudo chown mysql:mysql /var/log/mysql
sudo chmod 755 /var/log/mysql
Restart MySQL service
Restart MySQL to apply the binary logging configuration.
sudo systemctl restart mysql
sudo systemctl status mysql
Create backup user
Create a dedicated MySQL user with necessary privileges for backup operations.
sudo mysql -e "CREATE USER 'backup'@'localhost' IDENTIFIED BY 'StrongBackupPass123!';"
sudo mysql -e "GRANT SELECT, RELOAD, SHOW DATABASES, LOCK TABLES, REPLICATION CLIENT ON . TO 'backup'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
Create backup directory structure
Set up directories for storing full backups and binary log backups with proper permissions.
sudo mkdir -p /backup/mysql/{full,binlogs}
sudo chown -R mysql:mysql /backup/mysql
sudo chmod 750 /backup/mysql
Create full backup script
Create a script to perform consistent full backups using mysqldump with binary log position recording.
#!/bin/bash
set -euo pipefail
Configuration
BACKUP_DIR="/backup/mysql/full"
BACKUP_USER="backup"
BACKUP_PASS="StrongBackupPass123!"
RETENTION_DAYS=7
Create backup filename with timestamp
BACKUP_FILE="$BACKUP_DIR/mysql-full-$(date +%Y%m%d-%H%M%S).sql"
LOG_FILE="/var/log/mysql-backup.log"
Function for logging
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log "Starting full MySQL backup"
Create backup with consistent snapshot
mysqldump --user="$BACKUP_USER" --password="$BACKUP_PASS" \
--single-transaction \
--routines \
--triggers \
--master-data=2 \
--all-databases \
--flush-logs > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
log "Full backup completed: $BACKUP_FILE"
# Compress backup
gzip "$BACKUP_FILE"
log "Backup compressed: ${BACKUP_FILE}.gz"
# Extract binary log position from backup
BINLOG_INFO=$(zcat "${BACKUP_FILE}.gz" | grep "CHANGE MASTER TO" | head -1)
log "Binary log position: $BINLOG_INFO"
else
log "ERROR: Full backup failed"
exit 1
fi
Clean up old backups
find "$BACKUP_DIR" -name "mysql-full-*.sql.gz" -mtime +$RETENTION_DAYS -delete
log "Cleanup completed - removed backups older than $RETENTION_DAYS days"
log "Full backup process completed successfully"
Create binary log backup script
Create a script to safely backup and archive binary logs for point-in-time recovery.
#!/bin/bash
set -euo pipefail
Configuration
BINLOG_BACKUP_DIR="/backup/mysql/binlogs"
MYSQL_BINLOG_DIR="/var/log/mysql"
BACKUP_USER="backup"
BACKUP_PASS="StrongBackupPass123!"
RETENTION_DAYS=30
LOG_FILE="/var/log/mysql-binlog-backup.log"
Function for logging
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log "Starting binary log backup"
Get list of binary logs
BINLOGS=$(mysql --user="$BACKUP_USER" --password="$BACKUP_PASS" -e "SHOW BINARY LOGS" --batch --skip-column-names)
if [ -z "$BINLOGS" ]; then
log "No binary logs found"
exit 0
fi
Get current binary log file
CURRENT_BINLOG=$(mysql --user="$BACKUP_USER" --password="$BACKUP_PASS" -e "SHOW MASTER STATUS" --batch --skip-column-names | cut -f1)
Process each binary log except the current one
echo "$BINLOGS" | while read -r binlog_line; do
BINLOG_FILE=$(echo "$binlog_line" | awk '{print $1}')
# Skip current active binary log
if [ "$BINLOG_FILE" = "$CURRENT_BINLOG" ]; then
log "Skipping active binary log: $BINLOG_FILE"
continue
fi
# Check if already backed up
if [ -f "$BINLOG_BACKUP_DIR/$BINLOG_FILE" ]; then
log "Binary log already backed up: $BINLOG_FILE"
continue
fi
# Copy binary log
if cp "$MYSQL_BINLOG_DIR/$BINLOG_FILE" "$BINLOG_BACKUP_DIR/"; then
log "Backed up binary log: $BINLOG_FILE"
# Compress the backup
gzip "$BINLOG_BACKUP_DIR/$BINLOG_FILE"
log "Compressed binary log: $BINLOG_FILE.gz"
else
log "ERROR: Failed to backup binary log: $BINLOG_FILE"
fi
done
Flush logs to create a new binary log
mysql --user="$BACKUP_USER" --password="$BACKUP_PASS" -e "FLUSH LOGS"
log "Flushed binary logs"
Clean up old binary log backups
find "$BINLOG_BACKUP_DIR" -name "mysql-bin.*" -mtime +$RETENTION_DAYS -delete
log "Cleanup completed - removed binary log backups older than $RETENTION_DAYS days"
log "Binary log backup completed successfully"
Make backup scripts executable
Set proper permissions for the backup scripts to ensure they can be executed securely.
sudo chmod 750 /usr/local/bin/mysql-full-backup.sh
sudo chmod 750 /usr/local/bin/mysql-binlog-backup.sh
sudo chown mysql:mysql /usr/local/bin/mysql-*-backup.sh
Create point-in-time recovery script
Create a script to perform point-in-time recovery from full backup and binary logs.
#!/bin/bash
set -euo pipefail
Usage function
usage() {
echo "Usage: $0 -f -t [-d ]"
echo "Example: $0 -f /backup/mysql/full/mysql-full-20241201-120000.sql.gz -t '2024-12-01 14:30:00'"
exit 1
}
Parse command line arguments
FULL_BACKUP=""
TARGET_TIME=""
TARGET_DB=""
while getopts "f:t:d:h" opt; do
case $opt in
f) FULL_BACKUP="$OPTARG" ;;
t) TARGET_TIME="$OPTARG" ;;
d) TARGET_DB="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
if [ -z "$FULL_BACKUP" ] || [ -z "$TARGET_TIME" ]; then
usage
fi
Configuration
BINLOG_BACKUP_DIR="/backup/mysql/binlogs"
TEMP_DIR="/tmp/mysql-pitr-$$"
LOG_FILE="/var/log/mysql-pitr-restore.log"
Function for logging
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log "Starting point-in-time recovery to: $TARGET_TIME"
log "Using full backup: $FULL_BACKUP"
Create temporary directory
mkdir -p "$TEMP_DIR"
Extract binary log position from full backup
if [[ "$FULL_BACKUP" == *.gz ]]; then
BINLOG_INFO=$(zcat "$FULL_BACKUP" | grep "CHANGE MASTER TO" | head -1)
else
BINLOG_INFO=$(cat "$FULL_BACKUP" | grep "CHANGE MASTER TO" | head -1)
fi
BINLOG_FILE=$(echo "$BINLOG_INFO" | sed -n "s/.MASTER_LOG_FILE='\([^']\)'.*/\1/p")
BINLOG_POS=$(echo "$BINLOG_INFO" | sed -n "s/.MASTER_LOG_POS=\([0-9]\).*/\1/p")
log "Starting from binary log: $BINLOG_FILE at position: $BINLOG_POS"
Restore full backup
log "Restoring full backup..."
if [[ "$FULL_BACKUP" == *.gz ]]; then
zcat "$FULL_BACKUP" | mysql
else
mysql < "$FULL_BACKUP"
fi
log "Full backup restored successfully"
Apply binary logs up to target time
log "Applying binary logs up to $TARGET_TIME..."
Find all binary logs after the backup
BINLOG_FILES=$(find "$BINLOG_BACKUP_DIR" -name "mysql-bin.*" | sort)
for BINLOG_BACKUP in $BINLOG_FILES; do
CURRENT_BINLOG=$(basename "$BINLOG_BACKUP" .gz)
# Skip logs before our starting position
if [[ "$CURRENT_BINLOG" < "$BINLOG_FILE" ]]; then
continue
fi
log "Processing binary log: $CURRENT_BINLOG"
# Decompress if needed
if [[ "$BINLOG_BACKUP" == *.gz ]]; then
zcat "$BINLOG_BACKUP" > "$TEMP_DIR/$CURRENT_BINLOG"
BINLOG_PATH="$TEMP_DIR/$CURRENT_BINLOG"
else
BINLOG_PATH="$BINLOG_BACKUP"
fi
# Apply binary log with time limit
if [ "$CURRENT_BINLOG" = "$BINLOG_FILE" ]; then
# First log - start from position
mysqlbinlog --start-position="$BINLOG_POS" --stop-datetime="$TARGET_TIME" "$BINLOG_PATH" | mysql
else
# Subsequent logs - apply from beginning
mysqlbinlog --stop-datetime="$TARGET_TIME" "$BINLOG_PATH" | mysql
fi
log "Applied binary log: $CURRENT_BINLOG"
done
Cleanup
rm -rf "$TEMP_DIR"
log "Point-in-time recovery completed successfully to $TARGET_TIME"
log "Please verify your data and restart applications as needed"
Make recovery script executable
Set proper permissions for the point-in-time recovery script.
sudo chmod 750 /usr/local/bin/mysql-pitr-restore.sh
sudo chown mysql:mysql /usr/local/bin/mysql-pitr-restore.sh
Set up automated backup schedule
Configure systemd timers to automate full backups and binary log backups.
[Unit]
Description=MySQL Full Backup
After=mysql.service
Requires=mysql.service
[Service]
Type=oneshot
User=mysql
Group=mysql
ExecStart=/usr/local/bin/mysql-full-backup.sh
StandardOutput=journal
StandardError=journal
Create full backup timer
Schedule daily full backups during low-traffic hours.
[Unit]
Description=MySQL Full Backup Timer
Requires=mysql-full-backup.service
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1800
[Install]
WantedBy=timers.target
Create binary log backup service
Configure systemd service for binary log backups.
[Unit]
Description=MySQL Binary Log Backup
After=mysql.service
Requires=mysql.service
[Service]
Type=oneshot
User=mysql
Group=mysql
ExecStart=/usr/local/bin/mysql-binlog-backup.sh
StandardOutput=journal
StandardError=journal
Create binary log backup timer
Schedule hourly binary log backups for minimal data loss.
[Unit]
Description=MySQL Binary Log Backup Timer
Requires=mysql-binlog-backup.service
[Timer]
OnCalendar=hourly
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Enable and start backup timers
Enable the systemd timers to start the automated backup schedule.
sudo systemctl daemon-reload
sudo systemctl enable --now mysql-full-backup.timer
sudo systemctl enable --now mysql-binlog-backup.timer
Create backup monitoring script
Set up monitoring to alert when backups fail or lag behind schedule.
#!/bin/bash
set -euo pipefail
Configuration
FULL_BACKUP_DIR="/backup/mysql/full"
BINLOG_BACKUP_DIR="/backup/mysql/binlogs"
ALERT_EMAIL="admin@example.com"
MAX_FULL_BACKUP_AGE=25 # hours
MAX_BINLOG_BACKUP_AGE=2 # hours
Function to send alert
send_alert() {
local subject="$1"
local message="$2"
echo "$message" | mail -s "$subject" "$ALERT_EMAIL"
logger -t mysql-backup-monitor "ALERT: $subject - $message"
}
Check full backup freshness
LATEST_FULL=$(find "$FULL_BACKUP_DIR" -name "mysql-full-*.sql.gz" -type f -printf '%T@ %p\n' | sort -nr | head -1 | cut -d' ' -f2-)
if [ -z "$LATEST_FULL" ]; then
send_alert "MySQL Backup Alert" "No full backups found in $FULL_BACKUP_DIR"
else
FULL_AGE_HOURS=$(( ($(date +%s) - $(stat -c %Y "$LATEST_FULL")) / 3600 ))
if [ "$FULL_AGE_HOURS" -gt "$MAX_FULL_BACKUP_AGE" ]; then
send_alert "MySQL Backup Alert" "Latest full backup is $FULL_AGE_HOURS hours old (max: $MAX_FULL_BACKUP_AGE)"
fi
fi
Check binary log backup freshness
LATEST_BINLOG=$(find "$BINLOG_BACKUP_DIR" -name "mysql-bin.*" -type f -printf '%T@ %p\n' | sort -nr | head -1 | cut -d' ' -f2-)
if [ -z "$LATEST_BINLOG" ]; then
send_alert "MySQL Backup Alert" "No binary log backups found in $BINLOG_BACKUP_DIR"
else
BINLOG_AGE_HOURS=$(( ($(date +%s) - $(stat -c %Y "$LATEST_BINLOG")) / 3600 ))
if [ "$BINLOG_AGE_HOURS" -gt "$MAX_BINLOG_BACKUP_AGE" ]; then
send_alert "MySQL Backup Alert" "Latest binary log backup is $BINLOG_AGE_HOURS hours old (max: $MAX_BINLOG_BACKUP_AGE)"
fi
fi
Check disk space
BACKUP_USAGE=$(df "$FULL_BACKUP_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$BACKUP_USAGE" -gt 90 ]; then
send_alert "MySQL Backup Alert" "Backup disk usage is at ${BACKUP_USAGE}%"
fi
echo "Backup monitoring completed successfully"
Make monitoring script executable and schedule
Configure the monitoring script to run every hour and check backup health.
sudo chmod 755 /usr/local/bin/mysql-backup-monitor.sh
sudo chown mysql:mysql /usr/local/bin/mysql-backup-monitor.sh
# Monitor MySQL backups every hour
0 mysql /usr/local/bin/mysql-backup-monitor.sh >/dev/null 2>&1
Verify your setup
Test the binary logging configuration and backup procedures to ensure everything works correctly.
# Check binary logging is enabled
sudo mysql -e "SHOW VARIABLES LIKE 'log_bin';"
sudo mysql -e "SHOW BINARY LOGS;"
Test full backup
sudo -u mysql /usr/local/bin/mysql-full-backup.sh
Test binary log backup
sudo -u mysql /usr/local/bin/mysql-binlog-backup.sh
Check timer status
sudo systemctl status mysql-full-backup.timer
sudo systemctl status mysql-binlog-backup.timer
View backup files
ls -la /backup/mysql/full/
ls -la /backup/mysql/binlogs/
Check backup logs
sudo tail -f /var/log/mysql-backup.log
sudo tail -f /var/log/mysql-binlog-backup.log
Perform point-in-time recovery
Example recovery scenario
Here's how to perform point-in-time recovery to restore data before an accidental deletion.
# Example: Recover to 2024-12-01 14:30:00
Stop MySQL to prevent further changes
sudo systemctl stop mysql
Backup current data (optional but recommended)
sudo mv /var/lib/mysql /var/lib/mysql.backup
sudo mkdir /var/lib/mysql
sudo chown mysql:mysql /var/lib/mysql
Start MySQL for restoration
sudo systemctl start mysql
Run point-in-time recovery
sudo -u mysql /usr/local/bin/mysql-pitr-restore.sh \
-f /backup/mysql/full/mysql-full-20241201-120000.sql.gz \
-t '2024-12-01 14:30:00'
Restart MySQL
sudo systemctl restart mysql
Monitor and maintain binary logs
Set up log rotation monitoring
Monitor binary log disk usage and ensure logs rotate properly to prevent disk space issues.
# Check current binary log status
sudo mysql -e "SHOW MASTER STATUS;"
sudo mysql -e "SHOW BINARY LOGS;"
Check binary log disk usage
sudo du -sh /var/log/mysql/
Manually rotate logs if needed
sudo mysql -e "FLUSH LOGS;"
Purge old binary logs (keep last 3 days)
sudo mysql -e "PURGE BINARY LOGS BEFORE DATE(NOW() - INTERVAL 3 DAY);"
Performance monitoring queries
Monitor binary log performance impact and adjust configuration as needed.
# Check binary log write performance
sudo mysql -e "SHOW STATUS LIKE 'Binlog%';"
Monitor binary log cache usage
sudo mysql -e "SHOW STATUS LIKE 'Binlog_cache%';"
Check if sync_binlog is impacting performance
sudo mysql -e "SHOW STATUS LIKE 'Binlog_stmt_cache%';"
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Binary logs not created | log-bin not properly configured | Check /etc/mysql/mysql.conf.d/mysqld.cnf and restart MySQL |
| Backup script fails with permission denied | Incorrect file permissions | sudo chown mysql:mysql /backup/mysql && sudo chmod 750 /backup/mysql |
| Point-in-time recovery stops early | Missing binary log files | Check /backup/mysql/binlogs/ for complete log sequence |
| Disk space running out | Binary logs not rotating | Reduce expire_logs_days or max_binlog_size in MySQL config |
| Backup monitoring not working | Mail service not configured | Install and configure postfix or modify script to use other alerting |
| Recovery script can't find logs | Incorrect binary log path | Verify BINLOG_BACKUP_DIR matches actual backup location |
Next steps
- Set up MySQL backup monitoring with Prometheus alerts and Grafana dashboards
- Configure MySQL replication with binary log encryption for enhanced security
- Implement MySQL backup compression and remote storage with S3 integration
- Set up MySQL Galera cluster with automated backup rotation and monitoring
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
BACKUP_PASS="${MYSQL_BACKUP_PASSWORD:-$(openssl rand -base64 32)}"
MYSQL_CONFIG_FILE=""
MYSQL_SERVICE=""
BACKUP_DIR="/backup/mysql"
LOG_DIR="/var/log/mysql"
# Progress tracking
TOTAL_STEPS=9
CURRENT_STEP=0
# Functions
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error() {
echo -e "${RED}ERROR: $1${NC}" >&2
exit 1
}
warn() {
echo -e "${YELLOW}WARNING: $1${NC}" >&2
}
progress() {
((CURRENT_STEP++))
echo -e "${BLUE}[$CURRENT_STEP/$TOTAL_STEPS]${NC} $1"
}
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -p PASSWORD Set MySQL backup user password (default: auto-generated)"
echo " -h Show this help message"
exit 1
}
cleanup() {
warn "Script interrupted. Cleaning up..."
if systemctl is-active --quiet "$MYSQL_SERVICE" 2>/dev/null; then
systemctl restart "$MYSQL_SERVICE" 2>/dev/null || true
fi
}
# Parse arguments
while getopts "p:h" opt; do
case $opt in
p) BACKUP_PASS="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
# Set trap for cleanup
trap cleanup ERR INT TERM
# Check prerequisites
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root"
fi
if ! command -v openssl &> /dev/null; then
error "openssl is required but not installed"
fi
# Detect distribution
progress "Detecting Linux distribution"
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
MYSQL_CONFIG_FILE="/etc/mysql/mysql.conf.d/99-binlog.cnf"
MYSQL_SERVICE="mysql"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
MYSQL_CONFIG_FILE="/etc/my.cnf.d/binlog.cnf"
MYSQL_SERVICE="mysqld"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
MYSQL_CONFIG_FILE="/etc/my.cnf.d/binlog.cnf"
MYSQL_SERVICE="mysqld"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
MYSQL_CONFIG_FILE="/etc/my.cnf.d/binlog.cnf"
MYSQL_SERVICE="mysqld"
;;
*)
error "Unsupported distribution: $ID"
;;
esac
log "Detected distribution: $PRETTY_NAME"
else
error "Cannot detect Linux distribution"
fi
# Install MySQL
progress "Installing MySQL server"
if [[ "$PKG_MGR" == "apt" ]]; then
apt update
$PKG_INSTALL mysql-server mysql-client
else
$PKG_INSTALL mysql-server mysql
systemctl enable "$MYSQL_SERVICE"
if ! systemctl is-active --quiet "$MYSQL_SERVICE"; then
systemctl start "$MYSQL_SERVICE"
fi
fi
# Create binary log directory
progress "Creating binary log directory"
mkdir -p "$LOG_DIR"
chown mysql:mysql "$LOG_DIR"
chmod 755 "$LOG_DIR"
# Configure MySQL binary logging
progress "Configuring MySQL binary logging"
cat > "$MYSQL_CONFIG_FILE" << 'EOF'
[mysqld]
# Binary logging configuration
log-bin = /var/log/mysql/mysql-bin
server-id = 1
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M
sync_binlog = 1
# Enable GTID for better replication consistency
gtid_mode = ON
enforce_gtid_consistency = ON
log_slave_updates = ON
# InnoDB settings for durability
innodb_flush_log_at_trx_commit = 1
innodb_support_xa = 1
EOF
chmod 644 "$MYSQL_CONFIG_FILE"
# Restart MySQL service
progress "Restarting MySQL service"
systemctl restart "$MYSQL_SERVICE"
systemctl enable "$MYSQL_SERVICE"
# Wait for MySQL to be ready
sleep 5
if ! systemctl is-active --quiet "$MYSQL_SERVICE"; then
error "MySQL service failed to start"
fi
# Create backup user
progress "Creating backup user"
mysql -e "CREATE USER IF NOT EXISTS 'backup'@'localhost' IDENTIFIED BY '$BACKUP_PASS';"
mysql -e "GRANT SELECT, RELOAD, SHOW DATABASES, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'backup'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
# Create backup directory structure
progress "Creating backup directories"
mkdir -p "$BACKUP_DIR"/{full,binlogs}
chown -R mysql:mysql "$BACKUP_DIR"
chmod 750 "$BACKUP_DIR"
chmod 750 "$BACKUP_DIR/full"
chmod 750 "$BACKUP_DIR/binlogs"
# Create full backup script
progress "Creating full backup script"
cat > /usr/local/bin/mysql-full-backup.sh << EOF
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="$BACKUP_DIR/full"
BACKUP_USER="backup"
BACKUP_PASS="$BACKUP_PASS"
RETENTION_DAYS=7
BACKUP_FILE="\$BACKUP_DIR/mysql-full-\$(date +%Y%m%d-%H%M%S).sql"
LOG_FILE="/var/log/mysql-backup.log"
log() {
echo "\$(date '+%Y-%m-%d %H:%M:%S') - \$1" | tee -a "\$LOG_FILE"
}
log "Starting full MySQL backup"
mysqldump --user="\$BACKUP_USER" --password="\$BACKUP_PASS" \\
--single-transaction \\
--routines \\
--triggers \\
--master-data=2 \\
--all-databases \\
--flush-logs > "\$BACKUP_FILE"
if [ \$? -eq 0 ]; then
log "Full backup completed: \$BACKUP_FILE"
gzip "\$BACKUP_FILE"
log "Backup compressed: \${BACKUP_FILE}.gz"
BINLOG_INFO=\$(zcat "\${BACKUP_FILE}.gz" | grep "CHANGE MASTER TO" | head -1)
log "Binary log position: \$BINLOG_INFO"
else
log "ERROR: Full backup failed"
exit 1
fi
find "\$BACKUP_DIR" -name "mysql-full-*.sql.gz" -mtime +\$RETENTION_DAYS -delete
log "Cleanup completed - removed backups older than \$RETENTION_DAYS days"
log "Full backup process completed successfully"
EOF
chmod 755 /usr/local/bin/mysql-full-backup.sh
# Create binary log backup script
progress "Creating binary log backup script"
cat > /usr/local/bin/mysql-binlog-backup.sh << EOF
#!/usr/bin/env bash
set -euo pipefail
BINLOG_BACKUP_DIR="$BACKUP_DIR/binlogs"
MYSQL_BINLOG_DIR="$LOG_DIR"
BACKUP_USER="backup"
BACKUP_PASS="$BACKUP_PASS"
RETENTION_DAYS=30
LOG_FILE="/var/log/mysql-binlog-backup.log"
log() {
echo "\$(date '+%Y-%m-%d %H:%M:%S') - \$1" | tee -a "\$LOG_FILE"
}
log "Starting binary log backup"
mysql --user="\$BACKUP_USER" --password="\$BACKUP_PASS" -e "FLUSH LOGS;"
for binlog in \$MYSQL_BINLOG_DIR/mysql-bin.[0-9]*; do
if [ -f "\$binlog" ]; then
binlog_name=\$(basename "\$binlog")
if [ ! -f "\$BINLOG_BACKUP_DIR/\$binlog_name" ]; then
cp "\$binlog" "\$BINLOG_BACKUP_DIR/"
log "Backed up binary log: \$binlog_name"
fi
fi
done
find "\$BINLOG_BACKUP_DIR" -name "mysql-bin.*" -mtime +\$RETENTION_DAYS -delete
log "Binary log backup completed"
EOF
chmod 755 /usr/local/bin/mysql-binlog-backup.sh
# Verification
progress "Verifying installation"
log "Checking MySQL service status..."
systemctl is-active "$MYSQL_SERVICE" > /dev/null || error "MySQL service is not running"
log "Checking binary logging status..."
BINLOG_STATUS=$(mysql -e "SHOW VARIABLES LIKE 'log_bin';" | grep -c ON || true)
if [ "$BINLOG_STATUS" -eq 0 ]; then
error "Binary logging is not enabled"
fi
log "Checking backup user..."
mysql -u backup -p"$BACKUP_PASS" -e "SELECT 1;" > /dev/null || error "Backup user authentication failed"
log "Testing backup scripts..."
if ! /usr/local/bin/mysql-full-backup.sh; then
error "Full backup script test failed"
fi
echo
log "MySQL Point-in-Time Recovery setup completed successfully!"
echo
echo -e "${GREEN}Configuration Summary:${NC}"
echo "- MySQL service: $MYSQL_SERVICE"
echo "- Binary logs directory: $LOG_DIR"
echo "- Backup directory: $BACKUP_DIR"
echo "- Backup user: backup"
echo "- Backup password: $BACKUP_PASS"
echo
echo -e "${YELLOW}Next Steps:${NC}"
echo "1. Set up cron jobs for automated backups:"
echo " # Full backup daily at 2 AM"
echo " 0 2 * * * /usr/local/bin/mysql-full-backup.sh"
echo " # Binary log backup every hour"
echo " 0 * * * * /usr/local/bin/mysql-binlog-backup.sh"
echo
echo "2. Store the backup password securely"
echo "3. Test restore procedures"
Review the script before running. Execute with: bash install.sh