MariaDB Master-Slave Replication on Linux: Production Setup Guide
- Author :Liam K.
- Date :July 01, 2026
- Time :29 minutes
MariaDB replication copies data from a primary (master) server to one or more replica (slave) servers in near real time. It is the foundation for read scaling, geographic distribution, backups without locking the primary, and disaster recovery preparation. This guide configures GTID-based asynchronous replication — the modern standard that simplifies failover and eliminates the need to track binary log file positions manually.
Replication is not automatic failover. If the primary fails, a replica does not promote itself unless you configure orchestration (MariaDB MaxScale, Orchestrator, or manual procedure). Treat this guide as the replication layer — add failover tooling separately once replication is stable and monitored.
Replication Topology
- Primary (master) — accepts all writes, records changes in the binary log.
- Replica (slave) — pulls binlog events via replication thread, applies them locally.
- GTID — global transaction IDs make position tracking automatic across failovers.
- Semi-sync (optional) — waits for at least one replica to acknowledge before commit.
Prerequisites
- Two Linux servers: primary (10.0.0.10) and replica (10.0.0.20)
- MariaDB 10.6+ or 11.x installed on both servers
- Identical MariaDB major version on primary and replica
- Private network connectivity between servers on port 3306
- Existing database with data on the primary (or fresh install for greenfield)
Step 1: Configure the Primary Server
Enable binary logging and GTID on the primary. Assign a unique server-id. Use ROW binlog format for reliable replication with all storage engines.
sudo tee /etc/mysql/mariadb.conf.d/99-replication.cnf >/dev/null <<'EOF'
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_expire_logs_seconds = 604800
max_binlog_size = 256M
gtid_strict_mode = ON
[...]Step 2: Create Replication User
sudo mysql <<'SQL'
CREATE USER 'repl'@'10.0.0.20' IDENTIFIED BY 'REPLACE_WITH_STRONG_PASSWORD';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.20';
FLUSH PRIVILEGES;
SQLStep 3: Take a Consistent Backup for Initial Sync
The replica needs a point-in-time snapshot that matches the primary's binlog position (or GTID). Use mariadb-backup for hot backups on InnoDB tables without stopping writes.
sudo mariadb-backup --backup \
--target-dir=/backup/full \
--user=root \
--parallel=4
sudo mariadb-backup --prepare --target-dir=/backup/full
# Copy to replica:
rsync -avz /backup/full/ root@10.0.0.20:/backup/full/Step 4: Configure the Replica Server
sudo tee /etc/mysql/mariadb.conf.d/99-replication.cnf >/dev/null <<'EOF'
[mysqld]
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
relay_log = /var/log/mysql/relay-bin
read_only = ON
super_read_only = ON
[...]Step 5: Restore Backup and Start Replication
# On replica — restore the backup:
sudo systemctl stop mariadb
sudo rm -rf /var/lib/mysql/*
sudo mariadb-backup --copy-back --target-dir=/backup/full
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mariadb
# Start replication with GTID:
sudo mysql <<'SQL'
[...]Step 6: Verify Replication Health
sudo mysql -e "SHOW SLAVE STATUS\G" | grep -E 'Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_Error'
# Both should show:
# Slave_IO_Running: Yes
# Slave_SQL_Running: Yes
# Seconds_Behind_Master: 0 (or low single digits)Step 7: Route Read Queries to the Replica
Application-level read/write splitting is the simplest approach. Send SELECT queries to the replica connection and INSERT/UPDATE/DELETE to the primary. ORMs like Laravel, Django, and Sequelize support multiple database connections for this pattern.
# Laravel config/database.php example:
'mysql' => [
'read' => [
'host' => ['10.0.0.20'],
],
'write' => [
'host' => ['10.0.0.10'],
],
[...]Step 8: Monitor Replication Lag
Replication lag means the replica is behind the primary. Under load, a few seconds is normal. Sustained lag above 30 seconds indicates network issues, slow replica disk, or heavy write volume the replica cannot keep up with.
# Check lag from primary:
sudo mysql -e "SHOW SLAVE HOSTS;"
# On replica:
sudo mysql -e "SELECT TIMESTAMPDIFF(SECOND, ts, NOW()) AS lag_seconds FROM (SELECT MAX(ts) AS ts FROM mysql.gtid_slave_pos) t;"
# Alert if Seconds_Behind_Master > 30 for more than 5 minutesStep 9: Manual Failover Procedure (Document Before You Need It)
When the primary is unrecoverable, promote the replica. This is a manual process without orchestration — practice it in staging first.
# On replica (after confirming primary is dead):
sudo mysql <<'SQL'
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;
SQL
# Update application connection strings to point to 10.0.0.20
[...]Common Replication Errors
- Error 1236 — binlog position mismatch. Re-sync with a fresh backup and GTID.
- Error 1062 — duplicate key on replica. Data divergence — investigate before skipping events.
- High lag — replica disk too slow, long-running queries on replica, or network latency.
- GTID conflicts — never restore a random backup onto a replica with active GTID history.
Production Checklist
- Keep MariaDB versions identical on primary and replica.
- Restrict replication user to the replica IP only.
- Monitor
Seconds_Behind_Masterwith alerts above 30 seconds. - Run monthly failover drills in staging.
- Never write to the replica —
read_only=ONprevents accidents. - Retain binlogs for at least 7 days for point-in-time recovery.
"Replication gives you a copy of your data in real time — but only a tested failover runbook turns that copy into actual recovery capability."
Technical Author

System administrator and technical writer specializing in server infrastructure, security and deployment. Creating comprehensive guides to help you master server administration.