Set up production-grade MariaDB master-slave replication with SSL/TLS encryption for secure data transmission and MaxScale for automatic failover and load balancing.
Prerequisites
- Root or sudo access
- Two or more servers for master-slave setup
- Basic understanding of MariaDB administration
- Network connectivity between servers
- At least 2GB RAM per server
What this solves
MariaDB master-slave replication provides data redundancy and read scaling by maintaining synchronized copies of your database across multiple servers. This tutorial configures SSL-encrypted replication for secure data transmission and implements MaxScale for automatic failover, ensuring your database remains available even during primary server outages.
Step-by-step configuration
Update system packages
Start by updating your package manager and installing required dependencies for MariaDB 11.6 and SSL certificate management.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget gnupg2 software-properties-common openssl
Install MariaDB 11.6
Add the official MariaDB repository and install version 11.6 with the required packages for replication.
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=11.6
sudo apt install -y mariadb-server mariadb-client mariadb-backup
Secure MariaDB installation
Run the security script to remove test databases, set root password, and disable remote root access.
sudo systemctl enable --now mariadb
sudo mariadb-secure-installation
Generate SSL certificates
Create SSL certificates for encrypted replication between master and slave servers. We'll use a self-signed CA for this setup.
sudo mkdir -p /etc/mysql/ssl
cd /etc/mysql/ssl
Generate the Certificate Authority (CA) key and certificate:
sudo openssl genrsa 2048 > ca-key.pem
sudo openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem -subj "/C=US/ST=State/L=City/O=Organization/CN=MariaDB-CA"
Generate server certificate and key:
sudo openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=US/ST=State/L=City/O=Organization/CN=mariadb-server"
sudo openssl rsa -in server-key.pem -out server-key.pem
sudo openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
Generate client certificate and key:
sudo openssl req -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=US/ST=State/L=City/O=Organization/CN=mariadb-client"
sudo openssl rsa -in client-key.pem -out client-key.pem
sudo openssl x509 -req -in client-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem
Set correct permissions on SSL files:
sudo chown mysql:mysql /etc/mysql/ssl/*
sudo chmod 600 /etc/mysql/ssl/*-key.pem
sudo chmod 644 /etc/mysql/ssl/*-cert.pem
Configure MariaDB master server
Configure the master server with SSL support, binary logging, and replication settings.
[mysqld]
Server identification
server-id = 1
Binary logging for replication
log-bin = mysql-bin
log-bin-index = mysql-bin.index
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M
SSL Configuration
ssl-ca = /etc/mysql/ssl/ca-cert.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
require_secure_transport = ON
Network and security
bind-address = 0.0.0.0
port = 3306
Performance tuning
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
Replication settings
gtid_domain_id = 1
log_slave_updates = ON
replica_skip_errors = 1062
Configure MariaDB slave server
On the slave server, install MariaDB following the same steps, then configure it with a different server ID.
[mysqld]
Server identification (different from master)
server-id = 2
Binary logging for potential promotion
log-bin = mysql-bin
log-bin-index = mysql-bin.index
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M
SSL Configuration
ssl-ca = /etc/mysql/ssl/ca-cert.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
require_secure_transport = ON
Network and security
bind-address = 0.0.0.0
port = 3306
Performance tuning
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
Replication settings
gtid_domain_id = 1
log_slave_updates = ON
replica_skip_errors = 1062
read_only = ON
Restart MariaDB services
Restart both master and slave servers to apply the new configuration.
sudo systemctl restart mariadb
sudo systemctl status mariadb
Create replication user on master
Create a dedicated user for replication with SSL requirements and proper permissions.
sudo mariadb -u root -p
CREATE USER 'replication'@'%' IDENTIFIED BY 'StrongReplicationPassword123!' REQUIRE SSL;
GRANT REPLICATION SLAVE ON . TO 'replication'@'%';
FLUSH PRIVILEGES;
SHOW MASTER STATUS;
EXIT;
Configure replication on slave
Connect the slave to the master using SSL-encrypted replication. Replace the master host, log file, and position with your actual values.
sudo mariadb -u root -p
CHANGE MASTER TO
MASTER_HOST='203.0.113.10',
MASTER_USER='replication',
MASTER_PASSWORD='StrongReplicationPassword123!',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=328,
MASTER_SSL=1,
MASTER_SSL_CA='/etc/mysql/ssl/ca-cert.pem',
MASTER_SSL_CERT='/etc/mysql/ssl/client-cert.pem',
MASTER_SSL_KEY='/etc/mysql/ssl/client-key.pem';
START SLAVE;
SHOW SLAVE STATUS\G
EXIT;
Install and configure MaxScale
Install MaxScale for automatic failover and load balancing between master and slave servers.
curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --skip-server --mariadb-maxscale-version=24.02
sudo apt install -y maxscale
Create MaxScale monitoring user
Create a user on the master server for MaxScale to monitor server health and perform failover operations.
sudo mariadb -u root -p
CREATE USER 'maxscale'@'%' IDENTIFIED BY 'MaxScaleMonitorPassword123!' REQUIRE SSL;
GRANT SELECT ON mysql.user TO 'maxscale'@'%';
GRANT SELECT ON mysql.db TO 'maxscale'@'%';
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.columns_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.procs_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.proxies_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'%';
GRANT SHOW DATABASES ON . TO 'maxscale'@'%';
GRANT REPLICATION CLIENT ON . TO 'maxscale'@'%';
GRANT SUPER ON . TO 'maxscale'@'%';
FLUSH PRIVILEGES;
EXIT;
Configure MaxScale
Configure MaxScale with SSL support, monitoring, and automatic failover capabilities.
[maxscale]
threads=auto
log_warning=1
log_notice=1
log_info=0
log_debug=0
[server1]
type=server
address=203.0.113.10
port=3306
protocol=MariaDBBackend
ssl=required
ssl_cert=/etc/mysql/ssl/client-cert.pem
ssl_key=/etc/mysql/ssl/client-key.pem
ssl_ca_cert=/etc/mysql/ssl/ca-cert.pem
[server2]
type=server
address=203.0.113.11
port=3306
protocol=MariaDBBackend
ssl=required
ssl_cert=/etc/mysql/ssl/client-cert.pem
ssl_key=/etc/mysql/ssl/client-key.pem
ssl_ca_cert=/etc/mysql/ssl/ca-cert.pem
[MariaDB-Monitor]
type=monitor
module=mariadbmon
servers=server1,server2
user=maxscale
password=MaxScaleMonitorPassword123!
monitor_interval=2000
auto_failover=true
auto_rejoin=true
failover_timeout=30
switchover_timeout=30
[Read-Write-Service]
type=service
router=readwritesplit
servers=server1,server2
user=maxscale
password=MaxScaleMonitorPassword123!
master_failure_mode=fail_on_write
max_slave_connections=100%
max_slave_replication_lag=10
[Read-Only-Service]
type=service
router=readconnroute
router_options=slave
servers=server1,server2
user=maxscale
password=MaxScaleMonitorPassword123!
[Read-Write-Listener]
type=listener
service=Read-Write-Service
protocol=MariaDBClient
port=4006
address=0.0.0.0
[Read-Only-Listener]
type=listener
service=Read-Only-Service
protocol=MariaDBClient
port=4008
address=0.0.0.0
Copy SSL certificates to MaxScale
Copy the SSL certificates to MaxScale directory and set appropriate permissions.
sudo mkdir -p /etc/mysql/ssl
sudo cp /path/to/ssl/certificates/* /etc/mysql/ssl/
sudo chown maxscale:maxscale /etc/mysql/ssl/*
sudo chmod 600 /etc/mysql/ssl/*-key.pem
sudo chmod 644 /etc/mysql/ssl/*-cert.pem
Start and enable MaxScale
Start MaxScale and enable it to start automatically on boot.
sudo systemctl enable --now maxscale
sudo systemctl status maxscale
Configure firewall rules
Open necessary ports for MariaDB and MaxScale communication.
sudo ufw allow 3306/tcp comment "MariaDB"
sudo ufw allow 4006/tcp comment "MaxScale Read-Write"
sudo ufw allow 4008/tcp comment "MaxScale Read-Only"
sudo ufw allow from 203.0.113.0/24 to any port 3306 comment "MariaDB replication"
Verify your setup
Test the replication setup and MaxScale functionality.
# Check replication status on slave
sudo mariadb -u root -p -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|SSL_Allowed"
Test MaxScale connectivity
mariadb -h 127.0.0.1 -P 4006 -u root -p -e "SELECT @@hostname, @@server_id;"
Check MaxScale status
sudo maxctrl list servers
sudo maxctrl show server server1
Test SSL encryption
sudo mariadb -u root -p -e "SHOW STATUS LIKE 'Ssl_cipher';"
Test failover by creating test data on master
sudo mariadb -u root -p -e "CREATE DATABASE test_replication; USE test_replication; CREATE TABLE test (id INT PRIMARY KEY, data VARCHAR(50)); INSERT INTO test VALUES (1, 'replication test');"
Verify data appears on slave
sudo mariadb -h 203.0.113.11 -u root -p -e "USE test_replication; SELECT * FROM test;"
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Slave_IO_Running: No | SSL certificate issues or network connectivity | Check SSL certificate paths and permissions, verify network connectivity between servers |
| Slave_SQL_Running: No | Duplicate key errors or data inconsistency | Check error log, use SET GLOBAL sql_slave_skip_counter=1; START SLAVE; for single event skip |
| MaxScale connection refused | User permissions or SSL configuration | Verify maxscale user has correct permissions and SSL certificates are readable |
| SSL connection failed | Certificate mismatch or incorrect paths | Regenerate certificates ensuring correct CN and verify file paths in configuration |
| Replication lag increasing | High write load or inadequate slave resources | Monitor Seconds_Behind_Master, consider upgrading slave hardware or optimizing queries |
| Automatic failover not working | MaxScale monitor configuration or permissions | Check MaxScale logs, verify monitor user has SUPER privilege and auto_failover is enabled |
Next steps
- Set up automated MySQL database backups with compression and rotation
- Configure Nginx Redis caching with SSL authentication and security hardening
- Configure MariaDB Galera cluster for multi-master replication
- Implement MariaDB backup encryption with Mariabackup and automated restoration
- Configure MariaDB performance monitoring with Prometheus and Grafana dashboards
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# MariaDB 11.6 Master-Slave Replication Setup with SSL
# Production-quality installation script
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration variables
MARIADB_VERSION="11.6"
SSL_DIR="/etc/mysql/ssl"
MASTER_IP=""
SLAVE_TYPE=""
REPLICATION_USER="replicator"
REPLICATION_PASS=""
ROOT_PASS=""
usage() {
echo "Usage: $0 [master|slave] [options]"
echo "Options:"
echo " --master-ip IP Master server IP (required for slave setup)"
echo " --root-pass PASS MariaDB root password"
echo " --repl-pass PASS Replication user password"
echo "Example:"
echo " $0 master --root-pass strongpass123 --repl-pass replpass123"
echo " $0 slave --master-ip 192.168.1.10 --root-pass strongpass123 --repl-pass replpass123"
exit 1
}
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
cleanup() {
if [ $? -ne 0 ]; then
error "Script failed. Check logs above for details."
fi
}
trap cleanup ERR
check_root() {
if [ "$EUID" -ne 0 ]; then
error "Please run as root or with sudo"
fi
}
detect_distro() {
if [ ! -f /etc/os-release ]; then
error "Cannot detect Linux distribution"
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
MYSQL_CONF_DIR="/etc/mysql/mariadb.conf.d"
MYSQL_CONF_FILE="$MYSQL_CONF_DIR/50-server.cnf"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
MYSQL_CONF_DIR="/etc/my.cnf.d"
MYSQL_CONF_FILE="$MYSQL_CONF_DIR/server.cnf"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
MYSQL_CONF_DIR="/etc/my.cnf.d"
MYSQL_CONF_FILE="$MYSQL_CONF_DIR/server.cnf"
;;
*)
error "Unsupported distribution: $ID"
;;
esac
}
parse_args() {
if [ $# -lt 1 ]; then
usage
fi
SLAVE_TYPE="$1"
shift
if [ "$SLAVE_TYPE" != "master" ] && [ "$SLAVE_TYPE" != "slave" ]; then
usage
fi
while [[ $# -gt 0 ]]; do
case $1 in
--master-ip)
MASTER_IP="$2"
shift 2
;;
--root-pass)
ROOT_PASS="$2"
shift 2
;;
--repl-pass)
REPLICATION_PASS="$2"
shift 2
;;
*)
usage
;;
esac
done
if [ -z "$ROOT_PASS" ] || [ -z "$REPLICATION_PASS" ]; then
error "Root password and replication password are required"
fi
if [ "$SLAVE_TYPE" = "slave" ] && [ -z "$MASTER_IP" ]; then
error "Master IP is required for slave setup"
fi
}
update_system() {
echo "[1/8] Updating system packages..."
$PKG_UPDATE
$PKG_INSTALL curl wget gnupg2 openssl
if [ "$PKG_MGR" = "apt" ]; then
$PKG_INSTALL software-properties-common
fi
}
install_mariadb() {
echo "[2/8] Installing MariaDB $MARIADB_VERSION..."
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version="$MARIADB_VERSION"
$PKG_INSTALL mariadb-server mariadb-client mariadb-backup
systemctl enable mariadb
systemctl start mariadb
log "MariaDB installed and started"
}
generate_ssl_certs() {
echo "[3/8] Generating SSL certificates..."
mkdir -p "$SSL_DIR"
cd "$SSL_DIR"
# Generate CA key and certificate
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=MariaDB-CA"
# Generate server certificate
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=mariadb-server"
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
# Generate client certificate
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem -out client-req.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=mariadb-client"
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 02 -out client-cert.pem
# Set permissions
chown mysql:mysql "$SSL_DIR"/*
chmod 600 "$SSL_DIR"/*-key.pem
chmod 644 "$SSL_DIR"/*-cert.pem
rm -f "$SSL_DIR"/*.req
log "SSL certificates generated"
}
configure_mariadb() {
echo "[4/8] Configuring MariaDB..."
mkdir -p "$MYSQL_CONF_DIR"
local server_id
if [ "$SLAVE_TYPE" = "master" ]; then
server_id=1
else
server_id=2
fi
cat > "$MYSQL_CONF_FILE" << EOF
[mysqld]
# Server identification
server-id = $server_id
# Binary logging for replication
log-bin = mysql-bin
log-bin-index = mysql-bin.index
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M
# SSL Configuration
ssl-ca = $SSL_DIR/ca-cert.pem
ssl-cert = $SSL_DIR/server-cert.pem
ssl-key = $SSL_DIR/server-key.pem
require_secure_transport = ON
# Network and security
bind-address = 0.0.0.0
port = 3306
# Performance tuning
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
# Replication settings
gtid_domain_id = 1
log_slave_updates = ON
replica_skip_errors = 1062
EOF
systemctl restart mariadb
log "MariaDB configuration applied"
}
secure_mariadb() {
echo "[5/8] Securing MariaDB installation..."
mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$ROOT_PASS';"
mysql -u root -p"$ROOT_PASS" -e "DELETE FROM mysql.user WHERE User='';"
mysql -u root -p"$ROOT_PASS" -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');"
mysql -u root -p"$ROOT_PASS" -e "DROP DATABASE IF EXISTS test;"
mysql -u root -p"$ROOT_PASS" -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';"
mysql -u root -p"$ROOT_PASS" -e "FLUSH PRIVILEGES;"
log "MariaDB secured"
}
setup_replication() {
echo "[6/8] Setting up replication..."
if [ "$SLAVE_TYPE" = "master" ]; then
mysql -u root -p"$ROOT_PASS" -e "CREATE USER '$REPLICATION_USER'@'%' IDENTIFIED BY '$REPLICATION_PASS' REQUIRE SSL;"
mysql -u root -p"$ROOT_PASS" -e "GRANT REPLICATION SLAVE ON *.* TO '$REPLICATION_USER'@'%';"
mysql -u root -p"$ROOT_PASS" -e "FLUSH PRIVILEGES;"
log "Master replication user created"
warn "Copy SSL certificates from $SSL_DIR to slave server"
mysql -u root -p"$ROOT_PASS" -e "SHOW MASTER STATUS\G"
else
# Copy SSL certs would be manual step - warn user
warn "Ensure SSL certificates are copied from master server to $SSL_DIR"
log "Configure slave replication with:"
echo "mysql -u root -p'$ROOT_PASS' -e \"CHANGE MASTER TO MASTER_HOST='$MASTER_IP', MASTER_USER='$REPLICATION_USER', MASTER_PASSWORD='$REPLICATION_PASS', MASTER_USE_GTID=slave_pos, MASTER_SSL=1, MASTER_SSL_CA='$SSL_DIR/ca-cert.pem', MASTER_SSL_CERT='$SSL_DIR/client-cert.pem', MASTER_SSL_KEY='$SSL_DIR/client-key.pem';\""
echo "mysql -u root -p'$ROOT_PASS' -e \"START SLAVE;\""
fi
}
configure_firewall() {
echo "[7/8] Configuring firewall..."
if command -v ufw >/dev/null 2>&1; then
ufw allow 3306/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=3306/tcp
firewall-cmd --reload
fi
log "Firewall configured for MySQL port 3306"
}
verify_installation() {
echo "[8/8] Verifying installation..."
if ! systemctl is-active --quiet mariadb; then
error "MariaDB is not running"
fi
if ! mysql -u root -p"$ROOT_PASS" -e "SELECT VERSION();" >/dev/null 2>&1; then
error "Cannot connect to MariaDB with root password"
fi
if ! mysql -u root -p"$ROOT_PASS" -e "SHOW VARIABLES LIKE 'have_ssl';" | grep -q YES; then
error "SSL is not enabled in MariaDB"
fi
log "Installation verified successfully"
log "MariaDB $MARIADB_VERSION $SLAVE_TYPE server is ready"
if [ "$SLAVE_TYPE" = "slave" ]; then
warn "Remember to run the CHANGE MASTER and START SLAVE commands shown above"
fi
}
main() {
check_root
detect_distro
parse_args "$@"
update_system
install_mariadb
generate_ssl_certs
configure_mariadb
secure_mariadb
setup_replication
configure_firewall
verify_installation
log "MariaDB replication setup completed successfully!"
}
main "$@"
Review the script before running. Execute with: bash install.sh