KB
Databases

MySQL Master–Replica Setup (Ubuntu 20.04 + 22.04)

6 min read1269 words35 code blocks

At a glance#

  • Purpose: Configure asynchronous MySQL replication from a primary server to a replica.
  • Applies to: MySQL 8.0 on Ubuntu 20.04 and 22.04.
  • Risk: High - involves locking the primary and restarting the database.
  • Time: 1-2 hours.

Overview#

This document provides a complete guide to setting up MySQL Master–Replica (asynchronous replication) between two Ubuntu servers.

It includes installation, configuration, verification, and maintenance steps.


Server Details#

RoleHostnameIP AddressOSMySQL VersionData Directory
Mastermysql-master198.51.100.140Ubuntu 20.04 LTS8.0.42/var/lib/mysql/
Replicamysql-slave198.51.100.141Ubuntu 22.04 LTS8.0.43/var/lib/mysql/

Database replicated: rep_test


Step 1 — Install MySQL Server#

On Both Servers#

bash
sudo apt update
sudo apt install mysql-server -y

Enable and start the service:

bash
sudo systemctl enable mysql
sudo systemctl start mysql
sudo systemctl status mysql

Step 2 — Secure MySQL Installation#

Run the built-in security script:

bash
sudo mysql_secure_installation

Answer the prompts (recommended):

  • Validate password component → Y
  • Remove anonymous users → Y
  • Disallow root remote login → Y
  • Remove test database → Y
  • Reload privilege tables → Y

Step 3 — Configure the Master (198.51.100.140)#

Edit MySQL config file:

bash
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Find and update:

text
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin
binlog_do_db = rep_test
bind-address = 0.0.0.0

Save and restart MySQL:

bash
sudo systemctl restart mysql

Create replication user#

Log into MySQL:

bash
mysql -u root -p

Then run:

sql
CREATE USER 'replica_user'@'198.51.100.141' IDENTIFIED BY 'StrongPassword123!';
GRANT REPLICATION SLAVE ON *.* TO 'replica_user'@'198.51.100.141';
FLUSH PRIVILEGES;

Lock tables and check binary log#

sql
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;

Example output:

FilePositionBinlog_Do_DBBinlog_Ignore_DB
mysql-bin.0000011234rep_test

👉 Keep this session open (do not unlock yet).


Step 4 — Backup Master Database#

In a new terminal:

bash
mysqldump -u root -p --databases rep_test > /tmp/rep_test.sql

Transfer the dump to the replica:

bash
scp /tmp/rep_test.sql root@198.51.100.141:/tmp/

Return to the master session and unlock tables:

sql
UNLOCK TABLES;
EXIT;

Step 5 — Configure the Replica (198.51.100.141)#

Edit MySQL config:

bash
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Modify:

text
[mysqld]
server-id = 2
relay-log = /var/log/mysql/mysql-relay-bin

Save and restart MySQL:

bash
sudo systemctl restart mysql

Import the data from the master#

bash
mysql -u root -p < /tmp/rep_test.sql

Log into MySQL on the replica:

bash
mysql -u root -p

Configure replication (replace with your actual values from SHOW MASTER STATUS):

sql
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='198.51.100.140',
  SOURCE_USER='replica_user',
  SOURCE_PASSWORD='StrongPassword123!',
  SOURCE_LOG_FILE='mysql-bin.000001',
  SOURCE_LOG_POS=1234;

Start the replica:

sql
START REPLICA;

Check replication status:

sql
SHOW REPLICA STATUS\G

✅ Look for:

  • Replica_IO_Running: Yes
  • Replica_SQL_Running: Yes

Step 7 — Verify Replication#

On master:

sql
USE rep_test;
CREATE TABLE test_replica (id INT PRIMARY KEY, msg VARCHAR(50));
INSERT INTO test_replica VALUES (1, 'Replication successful!');

On replica:

sql
SELECT * FROM rep_test.test_replica;

You should see:

text
+----+----------------------+
| id | msg                  |
+----+----------------------+
|  1 | Replication successful! |
+----+----------------------+

Step 8 — Common Maintenance Commands#

ActionCommand
Check statusSHOW REPLICA STATUS\G
Stop replicationSTOP REPLICA;
Start replicationSTART REPLICA;
Restart MySQLsudo systemctl restart mysql
Monitor logtail -f /var/log/mysql/error.log

Step 9 — Troubleshooting#

IssueCauseFix
Replica_IO_Running: NoNetwork or credential issueVerify firewall, user, and IP
Replica_SQL_Running: NoSQL error on replicaCheck Last_SQL_Error
Replication stopsLog rotation or mismatchUse new log/pos from master
Duplicate dataIndependent changes on replicaRe-sync replica with fresh dump

Step 10 — Security Best Practices#

  • Use strong passwords for replication users.
  • Limit replication user access by IP.
  • Rotate credentials periodically.
  • Enable SSL replication if over WAN.
  • Keep OS and MySQL updated.

Reference Info#

SettingMasterReplica
server-id12
log_bin/var/log/mysql/mysql-bin
relay-log/var/log/mysql/mysql-relay-bin
Data Directory/var/lib/mysql//var/lib/mysql/

Final Result#

You now have a fully functional MySQL Master–Replica setup between:

  • Master: 198.51.100.140
  • Replica: 198.51.100.141
  • Database: rep_test

Overview#

This section documents the replication of a real-world sample database (employees) from the MySQL Master to the Replica.

It demonstrates that both schema and large-scale data changes (millions of rows) replicate successfully across the setup.


Server Details#

| Role | Hostname | IP Address | OS | MySQL Version |

|------|-----------|-------------|----------------|

| Master | mysql-master | 198.51.100.140 | Ubuntu 20.04 | 8.0.42 |

| Replica | mysql-slave | 198.51.100.141 | Ubuntu 22.04 | 8.0.43 |

Replication type: GTID-based Asynchronous Replication

Replication mode: Global (all databases replicate)


Step 1 — Download Employees Sample Database#

Run on master:

bash
cd /tmp
wget https://github.com/datacharmer/test_db/archive/refs/heads/master.zip
unzip master.zip
cd test_db-master

This downloads the official MySQL Employees sample dataset, which includes:

  • 300,000 employees
  • 4 million salary records
  • Department and title history

Step 2 — Import Database on Master#

Run:

bash
mysql -u root -p < employees.sql

This will:

  • Create the employees database automatically
  • Load all related tables and sample data

Step 3 — Verify Database on Master#

bash
mysql -u root -p -e "SHOW DATABASES;"

Expected:

text
+--------------------+
| Database           |
+--------------------+
| employees          |
| mysql              |
| performance_schema |
| rep_test           |
| sys                |
+--------------------+

Check table list:

bash
mysql -u root -p -e "SHOW TABLES IN employees;"

Step 4 — Monitor Replication on Replica#

While the import runs (it may take a few minutes), monitor replication status on the replica:

bash
watch -n 5 "mysql -u root -p -e 'SHOW REPLICA STATUS\G' | grep Seconds_Behind_Source"

You’ll see:

text
Seconds_Behind_Source: 15
Seconds_Behind_Source: 5
Seconds_Behind_Source: 0

When it reaches 0, replication is fully caught up.


Step 5 — Verify Database on Replica#

After replication catches up, confirm the database appeared automatically on the replica:

bash
mysql -u root -p -e "SHOW DATABASES;"

Expected:

text
+--------------------+
| Database           |
+--------------------+
| employees          |
| mysql              |
| performance_schema |
| rep_test           |
| sys                |
+--------------------+

Then check data consistency:

bash
mysql -u root -p -e "SELECT COUNT(*) FROM employees.employees;"

✅ Expected count: 300024

(same on both master and replica)


Step 6 — Explore the Data#

Example queries#

bash
mysql -u root -p -e "USE employees; SELECT * FROM employees LIMIT 10;"
mysql -u root -p -e "USE employees; SELECT * FROM departments;"
mysql -u root -p -e "USE employees; SELECT COUNT(*) FROM salaries;"

Example output#

text
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
| 10001  | 1953-09-02 | Georgi     | Facello   | M      | 1986-06-26 |
| 10002  | 1964-06-02 | Bezalel    | Simmel    | F      | 1985-11-21 |
| 10003  | 1959-12-03 | Parto      | Bamford   | M      | 1986-08-28 |
+--------+------------+------------+-----------+--------+------------+

Step 7 — Data Verification Summary#

CheckCommandExpected Result
Replication threadsSHOW REPLICA STATUS\GBoth IO and SQL running
Replication lagSeconds_Behind_Source0
Database presentSHOW DATABASES;employees appears
Row countSELECT COUNT(*) FROM employees.employees;Same on both servers
SchemaSHOW TABLES IN employees;Identical

Result#

  • The Employees database (millions of rows) replicated fully from master to replica.
  • Replication includes schema (CREATE DATABASE, CREATE TABLE) and data (inserts/updates).
  • Confirmed that global replication mode correctly syncs all databases.

Final Verification Snapshot#

ServerDatabaseTablesReplication LagStatus
Masteremployees6✅ Imported
Replicaemployees60s✅ Replicated

Notes#

  • MySQL now replicates all databases (no binlog_do_db filtering).
  • Schema and data replication are ensured via binlog_format = MIXED.
  • GTID-based replication allows recovery without reconfiguring file/position.