Setting up maxscale
This entry is more or less a kind of checklist for myself, because it’s difficult to find a decent all-encompassing guide for this.
Install MariaDB servers and set up replication
The Master DB
Lets begin with aligning the master. In /etc/mysql/ -> depending on the distro, you might have mariadb.conf.d or other similar folders.
Let’s add or adjust a few important variables:
[mysqld]
server-id=1
gtid_strict_mode=1
log_bin = /var/lib/mysql/bin-mariadb.log
expire_logs_days=8
sync_binlog = 1
binlog_format = row
log_slave_updates = 1
A few important details on these values:
server-id=1
: this is just a unique number for each MariaDB server on the network, maxscale will use this value in its own configuration file to refer to servers that can be added to a maxscale service definition, increment this value for each new DB server on the networkgtid_strict_mode=1
: this enforces identical binlogs across servers, and the replication is stopped entirely if any error is encountered, such as an accidental write on a replica server by a privileged user like rootlog_bin = /var/lib/mysql/bin-mariadb.log
: enabled binary logging, and defines the location and name of the binlog files, this is something that will need to be referenced when setting up replicationsync_binlog = 1
: syncs binlog content to disk before transactions are committed to the database. It allows for transactions to be rolled back in the case of a power cut, OS crash etc.expire_logs_days=8
: the amount of days after which logs are automatically purged, this is mainly a setting to help manage disk space on the serverbinlog_format=row
: each row change is logged individually, it is more verbose than statement, but is preferred for replication accuracylog_slave_updates=1
: Tells replicas to write changes they receive into their own binlogs, this is only needed for chained replication, or if wanting to use a replica as a new master during failover (so this is useful with maxscale)
Restart mariadb
sudo systemctl restart mariadb
Verify that the settings take effect (e.g. isn’t overwritten by something else in the .cnf file hierarchy)
e.g.
MariaDB [(none)]> SHOW VARIABLES LIKE "%gtid_strict%";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| gtid_strict_mode | ON |
+------------------+-------+
1 row in set (0.001 sec)
A few things to checK before proceeding:
skip-networking
: if this variable is set to 1, the server will limit connections to localhost, so comment this one out if it’s therebind-address
: This is sometimes set to 127.0.0.1 to allow TCP/IP connections only from localhost, changing it to 0.0.0.0 or commenting out the variable will allow the server to accept TCP/IP connections from anywhere
Finally, login to the Database as root, and set this variable, because maxscale will not do failovers without it:
CHANGE MASTER TO MASTER_USE_GTID = SLAVE_POS;
If you forget this step, messages about it in the logs of /var/log/maxscale/maxscale.log
should show up:
2025-04-18 20:41:51.874 error : [mariadbmon] (check_cluster_operations_support): The backend cluster does not support failover/switc
hover due to the following reason(s):\nReplica connection from server2 to [192.168.2.24]:3309 is not using gtid-replication.\n
Create a database user that handles the actual replication process:
CREATE USER 'repl'@'%' IDENTIFIED BY '<PASSWORD>';
GRANT REPLICATION SLAVE ON *.* to 'repl'@'%';
Finally, dump the DB so that it can be used as a starting point on the replica:
mysqldump --all-databases --master-data=2 --single-transaction -u root > dump.sql
The Replica DB
That brings us to the end of setting up the master DB. Now, make the same edits to my.cnf as was done for the master DB and restart the DB. Then, load the dumped data into the DB.
mysql -u root -p < /tmp/dump.sql
NOTE: If you’re already running maxscale at THIS stage for some reason, you’ll have divergent GTIDs and will need to fix it manually:
The steps to fix it: Go to master DB, and check the binlog position, make sure that NO writes are coming in to Master DB during this time:
MariaDB [(none)]> SHOW MASTER STATUS;
+--------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+--------------------+----------+--------------+------------------+
| bin-mariadb.000001 | 407858 | | |
+--------------------+----------+--------------+------------------+
1 row in set (0.000 sec)
Keep this info at hand, and return to the replica DB, and execute:
STOP SLAVE;
RESET MASTER;
CHANGE MASTER TO MASTER_LOG_FILE='bin-mariadb.000001', MASTER_LOG_POS=407858;
CHANGE MASTER TO MASTER_USE_GTID=slave_pos;
START SLAVE;
Note that the usage of RESET MASTER is generally not recommended, but this is OK when setting up a new server. Do not do this on any important server with real data in it.
If you’ve already got important data you DONT want to risk losing, then just undo the transaction(s) that caused the GTID divergence, and set MASTER_LOG_FILE
and MASTER_LOG_POS
WITHOUT doing RESET MASTER
.
With that caveat, if you’ve no such problems, just proceed with actually setting up the replication:
CHANGE MASTER TO
MASTER_HOST='<MASTER IP>',
MASTER_PORT=3309,
MASTER_USER='repl',
MASTER_PASSWORD='<PASSWORD>',
MASTER_USE_GTID=slave_pos;
START SLAVE;
Again, remembering to set the MASTER_USE_GTID
, which is needed for maxscale failovers.
Optionally, the replica server can be set to read only, so that writes will fail with an error instead of causing GTID divergence:
SET GLOBAL super_read_only=ON:
This way, not even root can make changes.
Maxscale
Before jumping into configuring maxscale, we’ll need to create a maxscale user on the Master DB (the user’s GRANTS should replicate down to the replica DBs)
CREATE USER 'maxscaleuser'@'%' IDENTIFIED BY '<PASSWORD>';
GRANT SELECT ON mysql.user TO 'maxscaleuser'@'%';
GRANT SELECT ON mysql.db TO 'maxscaleuser'@'%';
GRANT SELECT ON mysql.tables_priv TO 'maxscaleuser'@'%';
GRANT SELECT ON mysql.roles_mapping TO 'maxscaleuser'@'%';
GRANT SHOW DATABASES ON *.* TO 'maxscaleuser'@'%';
GRANT REPLICATION CLIENT on *.* to 'maxscaleuser'@'%';
GRANT SELECT ON mysql.* TO 'maxscaleuser'@'%';
GRANT SELECT ON mysql.columns_priv TO 'maxscaleuser'@'%';
GRANT SELECT ON mysql.procs_priv TO 'maxscaleuser'@'%';
GRANT SELECT ON mysql.proxies_priv TO 'maxscaleuser'@'%';
GRANT SLAVE MONITOR ON *.* to 'maxscaleuser'@'%';
GRANT SUPER ON *.* to 'maxscaleuser'@'%';
GRANT READ_ONLY ADMIN ON *.* TO 'maxscaleuser'@'%';
GRANT RELOAD ON *.* TO 'maxscaleuser'@'%';
Maxscale uses a .cnf format that works in a similar way to the one of mariadb.
The file should be located at /etc/maxscale.cnf
.
Some global params
Start by defining a few key global settings:
[maxscale]
threads=1
ms_timestamp = 1
syslog = 1
threads=1
-> This determines how many worker threads will handle events from the kernel, 1 is a safe number to set, and the actual number to be used really depends on the number of CPU cores available. It can also be set to auto, to let maxscale determine the number of worker threads for itself, but this should be done only on a server that’s dedicated to running nothing else but maxscale.ms_timestamp = 1
-> Setting it to 1 adds a higher precision to log timestamps.syslog = 1
-> Whether to log messages to syslog, helpful if using centralized logging systems.
There are many other potentially useful variables, governing further logging settings, changing default directories and so on.
Server definitions
This is the section for defining what MariaDB servers should maxscale be aware of and potentially route traffic to. The server-id values specified in each DB server’s my.cnf must match the internal server identity that maxscale uses here.
[server1]
type=server
address=192.168.2.24
port=3309
protocol=MariaDBBackend
[server2]
type=server
address=192.168.2.99
port=3306
protocol=MariaDBBackend
The only noteworthy param here is protocol, which specifies the communication protocol used between maxscale and the backend DBs.
Monitor for the servers
This keeps maxscale aware of server statuses, whether a server is down, whether GTID’s have diverged, etc. The monitor is essential for features like automated failover and rejoin.
[MariaDB-Monitor]
type=monitor
module=mariadbmon
servers=server1,server2
user=maxscaleuser
password=
replication_user=repl
replication_password=
auto_failover=true
auto_rejoin=true
enforce_simple_topology=true
enforce_read_only_slaves=1
monitor_interval=2000ms
enforce_read_only_slaves=1
-> This setting will cause the maxscale monitor to try and set read_only flag to ON to any replica server where it is OFF. This setting requires the SUPER privilege to be granted to the monitor user.enforce_simple_topology=1
-> This setting tells the monitor that there is one central Master DB from which everything else should replicate.auto_failover=true
-> Enables automated master failover. If the Master DB goes down, the monitor will try and promote one of the replica servers to become the new master. This requires the SUPER and RELOAD grants to the monitor DB user.auto_rejoin=true
-> Enables automated rejoining of previously downed masters back into the cluster as a replica when reachable by the monitor.monitor_interval
-> Determines how often maxscale tries to monitor the status of servers. The unit provided should be ms (milliseconds).
Service definitions
Here we can define what type of services maxscale should provide. In this example with two servers, we configure:
A read-only service that directs traffic to the replica (server2)
A read-write service that targets the current master (server1)
In more complex environments, it might make sense to have multiple different read services, to separate out different types of queries to different groups of replicas. For example, one might want to restrict heavy dwh analytical queries away to dedicated servers, to reduce resource usage on replicas used by applications.
[Read-Only-Service]
type=service
router=readconnroute
servers=server2
user=maxscaleuser
password=
router_options=slave,running
[Read-Write-Service]
type=service
router=readwritesplit
servers=server1
user=maxscaleuser
password=
router=readconnroute
-> A simple router that pics a server from the list based onrouter_options
.router_options=slave,running
-> Means the router should only pick running replicas.router=readwritesplit
-> Routes SELECT queries to replicas (if any are listed, but in this example, none are).
Listener definitions
The listeners define what port goes with which of the previously defined service. So rather than connecting to databases directly, users and applications will connect to these maxscale ports, and maxscale takes things from there.
[Read-Only-Listener]
type=listener
service=Read-Only-Service
protocol=MariaDBClient
port=4008
[Read-Write-Listener]
type=listener
service=Read-Write-Service
protocol=MariaDBClient
port=4006
Start maxscale
systemctl start maxscale
Test it all out!
If everything works as expected, maxscale should report a simple master-replica set up.
Use the following command to see that status of your servers via maxscale:
maxctrl list servers
┌─────────┬──────────────┬──────┬─────────────┬─────────────────┬─────────┬─────────────────┐
│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server1 │ 192.168.2.24 │ 3309 │ 0 │ Master, Running │ 0-1-800 │ MariaDB-Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server2 │ 192.168.2.99 │ 3306 │ 0 │ Slave, Running │ 0-1-800 │ MariaDB-Monitor │
└─────────┴──────────────┴──────┴─────────────┴─────────────────┴─────────┴─────────────────┘
To test the failover, stop the mariadb service on server1 and wait for the magic to happen:
systemctl stop mariadb
Now run the maxscale command again to observe
maxctrl list servers
It should show something like
┌─────────┬──────────────┬──────┬─────────────┬─────────────────┬─────────┬─────────────────┐
│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server1 │ 192.168.2.24 │ 3309 │ 0 │ Down │ 0-1-800 │ MariaDB-Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server2 │ 192.168.2.99 │ 3306 │ 0 │ Master, Running │ 0-1-800 │ MariaDB-Monitor │
└─────────┴──────────────┴──────┴─────────────┴─────────────────┴─────────┴─────────────────┘
Restart the downed server:
systemctl start mariadb
and then check again with
maxctrl list servers
and it should show the old master rejoining as a replica
┌─────────┬──────────────┬──────┬─────────────┬─────────────────┬─────────┬─────────────────┐
│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server1 │ 192.168.2.24 │ 3309 │ 0 │ Slave, Running │ 0-1-800 │ MariaDB-Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server2 │ 192.168.2.99 │ 3306 │ 0 │ Master, Running │ 0-1-800 │ MariaDB-Monitor │
└─────────┴──────────────┴──────┴─────────────┴─────────────────┴─────────┴─────────────────┘
We can make the switch back by downing server2:
┌─────────┬──────────────┬──────┬─────────────┬─────────────────┬─────────┬─────────────────┐
│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server1 │ 192.168.2.24 │ 3309 │ 0 │ Master, Running │ 0-1-800 │ MariaDB-Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server2 │ 192.168.2.99 │ 3306 │ 0 │ Down │ 0-1-800 │ MariaDB-Monitor │
└─────────┴──────────────┴──────┴─────────────┴─────────────────┴─────────┴─────────────────┘
and then starting it up again, bringing us back to our original state:
┌─────────┬──────────────┬──────┬─────────────┬─────────────────┬─────────┬─────────────────┐
│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server1 │ 192.168.2.24 │ 3309 │ 0 │ Master, Running │ 0-1-800 │ MariaDB-Monitor │
├─────────┼──────────────┼──────┼─────────────┼─────────────────┼─────────┼─────────────────┤
│ server2 │ 192.168.2.99 │ 3306 │ 0 │ Slave, Running │ 0-1-800 │ MariaDB-Monitor │
└─────────┴──────────────┴──────┴─────────────┴─────────────────┴─────────┴─────────────────┘
And that concludes this writeup.