Set Up a MariaDB Galera Cluster
Set up a synchronous three-node MariaDB Galera Cluster: gcomm addressing, bootstrap sequence, SST with mariabackup, garbd arbitrator, and split-brain prevention.
Before you start
- ▸Three servers with static IP addresses that can reach each other on the network
- ▸Root or sudo access on all nodes
- ▸MariaDB 10.6 or later available in your distro's repositories or the MariaDB official repo
- ▸Basic familiarity with MariaDB administration and systemd service management
Galera Cluster turns MariaDB into a synchronous multi-master cluster where every node holds a full copy of the data and any node accepts writes. This guide sets up a three-node cluster from scratch, covers the bootstrap sequence, arbitrator daemon (garbd), and explains how to handle split-brain scenarios. You need three servers that can reach each other on the network; two nodes is technically possible but leaves you permanently split-brain-prone, so three is the practical minimum.
Prerequisites and Planning
Each node needs MariaDB 10.6 or later with the Galera wsrep provider. Decide on your network topology before touching config files. You need two separate network paths if possible: one for client traffic, one for Galera replication (IST/SST). In this guide the nodes are:
- node1 — 192.168.10.11
- node2 — 192.168.10.12
- node3 — 192.168.10.13
Ports that must be open between all nodes: 3306 (MySQL client), 4444 (SST/rsync or mariabackup), 4567 (Galera replication, TCP+UDP), 4568 (IST).
Install MariaDB with Galera Support
Debian / Ubuntu
sudo apt update
sudo apt install -y mariadb-server galera-4 mariadb-backup
Fedora / RHEL 9 / Rocky Linux 9
sudo dnf install -y mariadb-server mariadb-server-galera mariadb-backup galera
Arch Linux
sudo pacman -S mariadb mariadb-clients galera
After installing, run the initial setup on each node if it hasn't happened automatically:
sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
Open Firewall Ports
firewalld (Fedora / RHEL / Rocky)
sudo firewall-cmd --permanent --add-port={3306,4444,4567,4568}/tcp
sudo firewall-cmd --permanent --add-port=4567/udp
sudo firewall-cmd --reload
ufw (Debian / Ubuntu)
sudo ufw allow from 192.168.10.0/24 to any port 3306,4444,4567,4568 proto tcp
sudo ufw allow from 192.168.10.0/24 to any port 4567 proto udp
sudo ufw reload
Configure Each Node
Create /etc/mysql/conf.d/galera.cnf (Debian/Ubuntu) or /etc/my.cnf.d/galera.cnf (RHEL family / Arch). The file below shows node1; change wsrep_node_address and wsrep_node_name on each host.
cat <<'EOF' | sudo tee /etc/mysql/conf.d/galera.cnf
[mysqld]
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
# Galera provider
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_name="prod_cluster"
wsrep_cluster_address="gcomm://192.168.10.11,192.168.10.12,192.168.10.13"
# Node-specific — change per host
wsrep_node_address="192.168.10.11"
wsrep_node_name="node1"
# SST method — mariabackup is hot-backup safe
wsrep_sst_method=mariabackup
wsrep_sst_auth="mariabackup:StrongSSTPwd"
# Performance tuning
wsrep_slave_threads=4
innodb_flush_log_at_trx_commit=0
EOF
On RHEL-family systems the provider path is typically /usr/lib64/galera/libgalera_smm.so. Verify with find /usr/lib* -name 'libgalera_smm.so' 2>/dev/null.
Create the SST User
Start MariaDB temporarily on each node in standalone mode (without wsrep) to create the backup user. The easiest approach is to start it once without the Galera config, create the user, then stop it.
sudo systemctl start mariadb
sudo mariadb -u root -p <<'SQL'
CREATE USER 'mariabackup'@'localhost' IDENTIFIED BY 'StrongSSTPwd';
GRANT PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'mariabackup'@'localhost';
FLUSH PRIVILEGES;
SQL
sudo systemctl stop mariadb
Bootstrap the First Node
Bootstrapping initializes a new cluster with a seqno (sequence number) of zero. Only do this once — on node1 — and never re-bootstrap a running cluster, or you will fork it.
sudo galera_new_cluster
This is a wrapper that starts MariaDB with --wsrep-new-cluster. Verify it is listening and reports a cluster size of 1:
sudo mariadb -u root -p -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
Expected output (size will grow as nodes join):
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 1 |
+--------------------+-------+
Join Remaining Nodes
On node2 and node3, simply start MariaDB normally. Galera reads wsrep_cluster_address, contacts an existing member, and triggers an SST (State Snapshot Transfer) if the joiner has no data, or a faster IST (Incremental State Transfer) if it is slightly behind.
sudo systemctl start mariadb
sudo systemctl enable mariadb
The node that sends the snapshot is called the donor; the receiving node is the joiner. During a full SST with mariabackup, the donor continues serving reads and writes normally. Watch the join progress on the joiner:
sudo journalctl -u mariadb -f
Once joined, check all three nodes are visible:
sudo mariadb -u root -p -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
Value should be 3. Also check wsrep_local_state_comment — it should read Synced on every node.
sudo mariadb -u root -p -e "SHOW STATUS LIKE 'wsrep_local_state_comment';"
Add an Arbitrator (garbd)
An even number of voting nodes risks split-brain. If you have exactly two MariaDB nodes, add garbd (Galera Arbitrator) on a third, lightweight host — it votes but stores no data.
# Debian/Ubuntu
sudo apt install -y galera-arbitrator-4
# RHEL family
sudo dnf install -y galera-arbitrator
Configure /etc/default/garb (Debian) or /etc/sysconfig/garb (RHEL):
GALERA_NODES="192.168.10.11:4567,192.168.10.12:4567"
GALERA_GROUP="prod_cluster"
GALERA_OPTIONS=""
LOG_FILE="/var/log/garbd.log"
sudo systemctl enable --now garb
Understanding Split-Brain
Galera uses a quorum model. A partition needs more than half the cluster's votes to remain primary. With three nodes, one partition of two nodes keeps quorum and continues operating; the single-node partition becomes non-primary and stops accepting writes — this is correct behavior.
A true split-brain occurs when the cluster partitions into two halves of equal weight and neither can reach quorum. With three nodes this cannot happen (2 vs 1 always gives one winner). With two nodes it is guaranteed on any network split, which is why garbd exists.
You can inspect component state with:
sudo mariadb -u root -p -e "SHOW STATUS LIKE 'wsrep_cluster_status';"
A non-primary node returns non-Primary and refuses DML. To recover a non-primary node after the network heals, simply restart MariaDB — it will rejoin automatically. If the entire cluster loses power simultaneously, bootstrap from the node with the highest seqno, found in /var/lib/mysql/grastate.dat.
cat /var/lib/mysql/grastate.dat
The node showing safe_to_bootstrap: 1 (set automatically by Galera on clean shutdown) or the highest seqno should be bootstrapped first. Never bootstrap a node with seqno: -1 — that indicates an unclean shutdown mid-transaction.
Verification
Write on node1, read on node3 to confirm synchronous replication:
# On node1
sudo mariadb -u root -p -e "CREATE DATABASE galera_test; CREATE TABLE galera_test.ping (id INT PRIMARY KEY, ts TIMESTAMP DEFAULT NOW()); INSERT INTO galera_test.ping (id) VALUES (1);"
# On node3
sudo mariadb -u root -p -e "SELECT * FROM galera_test.ping;"
The row written on node1 must appear on node3 immediately. Clean up with DROP DATABASE galera_test; on any node.
Troubleshooting
- Node stuck in Joining state: SST may be failing. Check
journalctl -u mariadbfor authentication errors or firewall blocks on port 4444. Verify themariabackupuser and password match inwsrep_sst_authand the grant. - wsrep_cluster_size stays at 1: The joining nodes cannot reach the seed node. Check
gcomm://addresses match actual IPs, and that port 4567 is open TCP and UDP. - Provider path error on startup: Run
find /usr/lib* -name 'libgalera_smm.so'and correctwsrep_providerin your config. - Deadlocks under heavy write load: Multi-master Galera uses optimistic locking; certification failures surface as deadlock errors to the client. Route writes to a single node, or use a load balancer like ProxySQL with writer/reader separation.
- garbd fails to start: Ensure
GALERA_GROUPmatcheswsrep_cluster_nameexactly — it is case-sensitive.
Frequently asked questions
- Can I run Galera Cluster with only two MariaDB nodes?
- Technically yes, but any network partition puts both nodes into equal halves with no quorum winner, causing both to stop accepting writes. Add garbd as a lightweight arbitrator on a third host to avoid this.
- What is the difference between SST and IST?
- SST (State Snapshot Transfer) copies the entire dataset from a donor to a joiner and is used when the joiner has no data or is too far behind. IST (Incremental State Transfer) sends only the missing write-set events and is much faster; it triggers automatically when the joiner's gcache covers the gap.
- How do I safely restart the entire cluster after a full power loss?
- Check /var/lib/mysql/grastate.dat on each node. Bootstrap from the node marked safe_to_bootstrap: 1, or if none is marked, the node with the highest seqno. Never bootstrap from a node with seqno: -1.
- Does Galera protect against write conflicts between nodes?
- Yes, through optimistic locking and certification. Transactions from different nodes that touch the same rows in the same order may conflict; the loser is rolled back and the client receives a deadlock error. In high-conflict workloads, routing all writes to one node eliminates this.
- Can I add a fourth node to the cluster without downtime?
- Yes. Configure the new node with the same galera.cnf, add its IP to wsrep_cluster_address on all existing nodes (a reload is not required immediately — gcomm auto-discovers members), then start MariaDB on the new node. It joins via SST or IST automatically.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.