Set up automated MySQL database backups with compression and rotation

Intermediate 25 min Apr 03, 2026 24 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Create production-grade MySQL backup automation with gzip compression, logrotate management, and systemd timer scheduling. Includes integrity checking and email notifications for reliable database protection.

Prerequisites

  • MySQL server installed and running
  • Root or sudo access
  • Basic understanding of Linux file permissions

What this solves

Automated MySQL backups protect your database from data loss while managing storage space efficiently. This tutorial sets up compressed backups with automated rotation, integrity verification, and failure notifications to ensure your MySQL data is consistently protected without manual intervention.

Step-by-step configuration

Install required packages

Install MySQL client tools and email utilities for backup operations and notifications.

sudo apt update
sudo apt install -y mysql-client gzip mailutils logrotate
sudo dnf install -y mysql mailx gzip logrotate

Create backup user and directory structure

Create a dedicated backup user and secure directory structure for storing backup files.

sudo useradd -r -s /bin/false backup-user
sudo mkdir -p /opt/mysql-backups/{daily,logs}
sudo chown -R backup-user:backup-user /opt/mysql-backups
sudo chmod 750 /opt/mysql-backups
sudo chmod 755 /opt/mysql-backups/daily /opt/mysql-backups/logs

Configure MySQL authentication

Create a MySQL user with backup privileges and configure secure authentication.

sudo mysql -u root -p

Run these commands in the MySQL prompt:

CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'BackupP@ssw0rd2024!';
GRANT SELECT, SHOW VIEW, RELOAD, REPLICATION CLIENT, EVENT, TRIGGER ON . TO 'backup_user'@'localhost';
GRANT LOCK TABLES ON . TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Create MySQL credentials file

Store MySQL credentials securely in a configuration file with restricted permissions.

sudo -u backup-user touch /home/backup-user/.my.cnf
[mysqldump]
user=backup_user
password=BackupP@ssw0rd2024!
host=localhost
single-transaction=true
routines=true
triggers=true
set-gtid-purged=OFF
sudo chown backup-user:backup-user /home/backup-user/.my.cnf
sudo chmod 600 /home/backup-user/.my.cnf

Create the backup script

Create a comprehensive backup script with compression, logging, and integrity checking.

#!/bin/bash

MySQL Backup Script with Compression and Logging

Author: System Administrator

Date: $(date +%Y-%m-%d)

set -euo pipefail

Configuration

BACKUP_DIR="/opt/mysql-backups/daily" LOG_DIR="/opt/mysql-backups/logs" LOG_FILE="$LOG_DIR/backup-$(date +%Y-%m-%d).log" RETENTION_DAYS=7 EMAIL_RECIPIENT="admin@example.com" HOSTNAME=$(hostname)

Function to log messages

log_message() { local level=$1 shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE" }

Function to send email notification

send_notification() { local subject="$1" local body="$2" echo "$body" | mail -s "$subject" "$EMAIL_RECIPIENT" 2>/dev/null || true }

Start logging

log_message "INFO" "Starting MySQL backup on $HOSTNAME"

Get list of databases (excluding system databases)

DATABASES=$(mysql --defaults-file=/home/backup-user/.my.cnf -e "SHOW DATABASES;" | grep -Ev '^(Database|information_schema|performance_schema|mysql|sys)$') if [ -z "$DATABASES" ]; then log_message "WARNING" "No user databases found to backup" exit 0 fi

Create timestamped backup directory

BACKUP_TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_SESSION_DIR="$BACKUP_DIR/$BACKUP_TIMESTAMP" mkdir -p "$BACKUP_SESSION_DIR"

Backup each database

BACKUP_SUCCESS=true for DB in $DATABASES; do log_message "INFO" "Starting backup of database: $DB" BACKUP_FILE="$BACKUP_SESSION_DIR/${DB}_${BACKUP_TIMESTAMP}.sql" COMPRESSED_FILE="${BACKUP_FILE}.gz" # Perform backup with compression if mysqldump --defaults-file=/home/backup-user/.my.cnf \ --single-transaction \ --routines \ --triggers \ --set-gtid-purged=OFF \ --verbose \ "$DB" | gzip > "$COMPRESSED_FILE"; then # Verify compressed file if gzip -t "$COMPRESSED_FILE" 2>/dev/null; then BACKUP_SIZE=$(du -h "$COMPRESSED_FILE" | cut -f1) log_message "INFO" "Successfully backed up $DB (Size: $BACKUP_SIZE)" else log_message "ERROR" "Backup verification failed for $DB - corrupted gzip file" rm -f "$COMPRESSED_FILE" BACKUP_SUCCESS=false fi else log_message "ERROR" "Failed to backup database: $DB" BACKUP_SUCCESS=false fi done

Clean up old backups

log_message "INFO" "Cleaning up backups older than $RETENTION_DAYS days" find "$BACKUP_DIR" -type d -name "[0-9]_[0-9]" -mtime +$RETENTION_DAYS -exec rm -rf {} + 2>/dev/null || true

Calculate total backup size

TOTAL_SIZE=$(du -sh "$BACKUP_SESSION_DIR" 2>/dev/null | cut -f1 || echo "Unknown")

Send notification based on backup status

if [ "$BACKUP_SUCCESS" = true ]; then log_message "INFO" "MySQL backup completed successfully (Total size: $TOTAL_SIZE)" send_notification "[SUCCESS] MySQL Backup - $HOSTNAME" "MySQL backup completed successfully on $HOSTNAME\n\nBackup location: $BACKUP_SESSION_DIR\nTotal size: $TOTAL_SIZE\nTimestamp: $BACKUP_TIMESTAMP" else log_message "ERROR" "MySQL backup completed with errors" send_notification "[ERROR] MySQL Backup Failed - $HOSTNAME" "MySQL backup completed with errors on $HOSTNAME\n\nPlease check the log file: $LOG_FILE\nTimestamp: $BACKUP_TIMESTAMP" exit 1 fi log_message "INFO" "Backup process finished"

Set script permissions and ownership

Configure secure permissions for the backup script to ensure only the backup user can execute it.

sudo chown backup-user:backup-user /opt/mysql-backups/backup-script.sh
sudo chmod 750 /opt/mysql-backups/backup-script.sh

Configure log rotation

Set up logrotate to manage backup log files and prevent disk space issues.

/opt/mysql-backups/logs/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
    su backup-user backup-user
    postrotate
        /bin/systemctl reload rsyslog > /dev/null 2>&1 || true
    endscript
}

Create systemd service unit

Create a systemd service to run the backup script with proper user context and security restrictions.

[Unit]
Description=MySQL Database Backup Service
Wants=network-online.target mysql.service
After=network-online.target mysql.service

[Service]
Type=oneshot
User=backup-user
Group=backup-user
ExecStart=/opt/mysql-backups/backup-script.sh
WorkingDirectory=/opt/mysql-backups
PrivateTmp=yes
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/opt/mysql-backups
StandardOutput=journal
StandardError=journal

Create systemd timer unit

Configure a systemd timer to run backups daily at 2 AM with randomized delay to prevent resource conflicts.

[Unit]
Description=MySQL Database Backup Timer
Requires=mysql-backup.service

[Timer]
OnCalendar=--* 02:00:00
RandomizedDelaySec=600
Persistent=true

[Install]
WantedBy=timers.target

Enable and start the backup timer

Reload systemd configuration and enable the backup timer to start automatically on boot.

sudo systemctl daemon-reload
sudo systemctl enable mysql-backup.timer
sudo systemctl start mysql-backup.timer

Test the backup system

Run a test backup to verify the configuration and check for any issues.

sudo systemctl start mysql-backup.service
sudo systemctl status mysql-backup.service

Verify your setup

Check the timer status and verify backup files are created correctly.

sudo systemctl status mysql-backup.timer
sudo systemctl list-timers mysql-backup.timer
ls -la /opt/mysql-backups/daily/
ls -la /opt/mysql-backups/logs/

Verify backup file integrity:

find /opt/mysql-backups/daily -name "*.gz" -type f -exec gzip -t {} \;
find /opt/mysql-backups/daily -name "*.gz" -type f -exec du -h {} \;

Check journal logs for service execution:

sudo journalctl -u mysql-backup.service --since today
sudo journalctl -u mysql-backup.timer --since "1 week ago"

Common issues

Symptom Cause Fix
Permission denied accessing backup directory Incorrect ownership or permissions sudo chown -R backup-user:backup-user /opt/mysql-backups && sudo chmod 750 /opt/mysql-backups
MySQL authentication failed Wrong credentials or missing privileges Verify /home/backup-user/.my.cnf and MySQL user permissions
Email notifications not working Mail system not configured Install and configure postfix: sudo dpkg-reconfigure postfix
Timer not triggering backups Timer not enabled or systemd issue sudo systemctl enable mysql-backup.timer && sudo systemctl start mysql-backup.timer
Backup files growing too large No cleanup or long retention period Adjust RETENTION_DAYS in backup script or add size-based cleanup
Corrupted backup files Insufficient disk space or I/O errors Check disk space with df -h and test with gzip -t filename.gz

Next steps

Automated install script

Run this to automate the entire setup

#MySQL #backup automation #database compression #mysqldump #systemd timers

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer