This post is also available in: Spanish
Percona XtraDB Cluster, commonly known as PXC, is a high-availability MySQL solution based on Percona Server for MySQL and Galera replication.
Unlike a traditional MySQL primary-replica architecture, every node in a Percona XtraDB Cluster contains a complete copy of the database and can participate in transaction processing.
This makes PXC especially interesting for DevOps, SRE, and Database Reliability Engineering environments where high availability, automated recovery, rolling maintenance, and failure handling are important.
In this guide, we will build a three-node Percona XtraDB Cluster lab and explore not only how to install it, but also how the cluster behaves when nodes fail, replication falls behind, or quorum is lost.
The goal is to create a lab where we can safely break things and understand how Percona behaves under real operational scenarios.
Percona XtraDB Cluster Architecture
A basic PXC architecture looks like this:
Application | HAProxy | +----------+----------+ | | | PXC01 PXC02 PXC03 MySQL MySQL MySQL \ | / \------ Galera -----/Each node contains a full copy of the database.
This differs from traditional MySQL replication, where one primary typically handles writes and replicas asynchronously receive changes:
Primary | | asynchronous replication vReplicaWith Percona XtraDB Cluster, transactions are replicated using Galera's write-set replication.
Although PXC supports writes through multiple nodes, that does not necessarily mean an application should randomly distribute writes across every database server.
A common production architecture is to use a preferred writer while keeping the remaining nodes available for failover:
HAProxy / ProxySQL | Writes | PXC01 / \ PXC02 PXC03This reduces the probability of transaction conflicts while still providing high availability.
How Transactions Work in PXC
Imagine an application executes the following transaction against pxc01:
BEGIN; UPDATE accountsSET balance = balance - 100WHERE id = 10; COMMIT;The transaction executes locally first.
Before it is committed, Galera creates a write-set describing the changes made by the transaction.
The write-set is distributed to the other members of the cluster:
PXC01 |Transaction executes | vWrite-set generated | +------------+ | | v vPXC02 PXC03 | |Certify Certify \ / \----------/ | CommitEach node performs a process known as certification.
Certification determines whether another concurrent transaction modified the same data.
For example:
PXC01 PXC02 UPDATE users UPDATE usersSET name='Alice' SET name='Bob'WHERE id=10 WHERE id=10 \ / \ / Certification | Conflict detected | One transaction abortsThis behavior is one reason multi-primary replication should be used carefully.
Applications generating many concurrent writes against the same rows can experience certification conflicts.
Why a PXC Cluster Should Have Three Nodes
One of the most important concepts in Galera-based clusters is quorum.
Consider a three-node cluster:
PXC01 ---- PXC02 ---- PXC03 1 1 1The cluster has three voting members.
To remain operational, a majority of the cluster must remain connected.
With three nodes:
3 nodes available → quorum2 nodes available → quorum1 node available → no quorumIf one node fails:
PXC01 -------- PXC02 X PXC03 2 / 3 nodes availableThe remaining two nodes still represent a majority, so the cluster continues processing transactions.
If another node fails:
PXC01 X PXC02X PXC03The remaining node no longer has quorum.
You may see states such as:
wsrep_cluster_status = Non-Primarywsrep_ready = OFFThe database intentionally stops accepting normal application traffic.
This protects the cluster against one of the most dangerous problems in distributed databases:
split brain.
Without quorum protection, two isolated sides of a network partition could both believe they are authoritative and accept conflicting writes.
Understanding IST and SST
Another important PXC concept is how a node synchronizes after being offline.
Imagine our cluster starts healthy:
PXC01 PXC02 PXC03 ✓ ✓ ✓Now pxc03 goes offline:
PXC01 PXC02 PXC03 ✓ ✓ XApplications continue generating transactions while the node is unavailable.
When pxc03 returns, it must synchronize with the cluster.
There are two primary mechanisms for this.
Incremental State Transfer — IST
If another cluster member still has the missing write-sets in its Galera cache, the returning node can receive only the transactions it missed.
PXC01 GCache TX1001TX1002TX1003TX1004TX1005 | | Missing transactions v PXC03This process is called:
Incremental State Transfer, or IST.
IST is normally much faster because it does not require transferring the entire database.
State Snapshot Transfer — SST
If the returning node has been offline long enough that the required transactions are no longer available in GCache, a complete copy of the database must be transferred.
PXC01 Full database | | v PXC03This process is called:
State Snapshot Transfer, or SST.
SST can place considerably more load on storage, networking, and the donor node than IST.
Understanding the relationship between:
ISTSSTGCacheDonorJoineris important when operating Percona clusters.
Building the Lab
For this lab we will use four virtual machines:
Server IP Address Purpose pxc01 192.168.50.11 Percona nodepxc02 192.168.50.12 Percona nodepxc03 192.168.50.13 Percona nodemysql-proxy 192.168.50.10 HAProxyA reasonable configuration for each database VM is:
2 vCPU4 GB RAM30–50 GB diskUbuntu 24.04For learning purposes, virtual machines are preferable to containers because they allow us to reproduce infrastructure-level failures such as:
Network partitions
VM crashes
Disk exhaustion
Storage latency
Firewall problems
Rolling operating-system maintenance
Required Network Ports
PXC nodes need to communicate over several ports.
3306 MySQL client connections4444 State Snapshot Transfer4567 Galera replication4568 Incremental State TransferIf UFW is enabled:
sudo ufw allow 3306/tcpsudo ufw allow 4444/tcpsudo ufw allow 4567/tcpsudo ufw allow 4567/udpsudo ufw allow 4568/tcpIn a real environment, these rules should be restricted to the database network instead of being accessible from every source.
Configure the Hostnames
On the first node:
sudo hostnamectl set-hostname pxc01On the second:
sudo hostnamectl set-hostname pxc02And on the third:
sudo hostnamectl set-hostname pxc03Add the nodes to /etc/hosts:
192.168.50.11 pxc01192.168.50.12 pxc02192.168.50.13 pxc03Verify connectivity between all three nodes before continuing.
Install Percona XtraDB Cluster
Perform the installation on every database node.
sudo apt update sudo apt install -y \ wget \ gnupg2 \ lsb-release \ curlDownload the Percona repository package:
wget https://repo.percona.com/apt/percona-release_latest.generic_all.debInstall it:
sudo dpkg -i percona-release_latest.generic_all.debEnable the PXC repository:
sudo percona-release setup pxc-84-ltsUpdate the package list:
sudo apt updateInstall Percona XtraDB Cluster:
sudo apt install -y percona-xtradb-clusterAfter installation, stop MySQL on all three nodes:
sudo systemctl stop mysqlWe want to configure the cluster before starting it.
Configure PXC01
First inspect the MySQL configuration directories:
ls -la /etc/mysql/ls -la /etc/mysql/mysql.conf.d/Create a PXC configuration file:
sudo nano /etc/mysql/mysql.conf.d/pxc.cnfFor pxc01:
[mysqld] server-id=1 datadir=/var/lib/mysqluser=mysql default_storage_engine=InnoDBinnodb_autoinc_lock_mode=2 wsrep_provider=/usr/lib/libgalera_smm.so wsrep_cluster_name=pxc-lab wsrep_cluster_address=gcomm://192.168.50.11,192.168.50.12,192.168.50.13 wsrep_node_name=pxc01wsrep_node_address=192.168.50.11 wsrep_sst_method=cloneThe most important settings here are:
wsrep_cluster_namewsrep_cluster_addresswsrep_node_namewsrep_node_addressThese tell Galera which cluster the server belongs to and how to discover the other nodes.
Configure PXC02
Use the same configuration with node-specific values:
[mysqld] server-id=2 datadir=/var/lib/mysqluser=mysql default_storage_engine=InnoDBinnodb_autoinc_lock_mode=2 wsrep_provider=/usr/lib/libgalera_smm.so wsrep_cluster_name=pxc-lab wsrep_cluster_address=gcomm://192.168.50.11,192.168.50.12,192.168.50.13 wsrep_node_name=pxc02wsrep_node_address=192.168.50.12 wsrep_sst_method=cloneConfigure PXC03
For the third server:
[mysqld] server-id=3 datadir=/var/lib/mysqluser=mysql default_storage_engine=InnoDBinnodb_autoinc_lock_mode=2 wsrep_provider=/usr/lib/libgalera_smm.so wsrep_cluster_name=pxc-lab wsrep_cluster_address=gcomm://192.168.50.11,192.168.50.12,192.168.50.13 wsrep_node_name=pxc03wsrep_node_address=192.168.50.13 wsrep_sst_method=cloneBootstrap the Cluster
The first node must be started differently from the other members.
On pxc01:
sudo systemctl start mysql@bootstrapBootstrapping tells Galera that this server is creating a new Primary Component rather than attempting to join an existing cluster.
Connect to MySQL:
mysql -uroot -pCheck the cluster status:
SHOW STATUS LIKE 'wsrep_cluster_size'; SHOW STATUS LIKE 'wsrep_cluster_status'; SHOW STATUS LIKE 'wsrep_local_state_comment'; SHOW STATUS LIKE 'wsrep_ready';A healthy first node should report something similar to:
wsrep_cluster_size 1wsrep_cluster_status Primarywsrep_local_state_comment Syncedwsrep_ready ONAdd PXC02
Now start MySQL normally on the second node:
sudo systemctl start mysqlWatch the logs:
journalctl -u mysql -fThe node should discover pxc01 and synchronize its state.
Check the cluster:
SHOW STATUS LIKE 'wsrep_cluster_size';The expected result is:
2Also check:
SHOW STATUS LIKE 'wsrep_local_state_comment';The node should eventually report:
SyncedAdd PXC03
Start MySQL normally:
sudo systemctl start mysqlCheck the cluster again:
SHOW STATUS LIKE 'wsrep_cluster_size';You should now have:
3Verify:
SHOW STATUS LIKE 'wsrep_cluster_status';The expected state is:
PrimaryOur three-node cluster is now operational.
Test Replication
Connect to pxc01 and create a database:
CREATE DATABASE dbre_lab; USE dbre_lab;Create a table:
CREATE TABLE transactions ( id BIGINT PRIMARY KEY AUTO_INCREMENT, description VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);Insert a record:
INSERT INTO transactions(description)VALUES ('Inserted through pxc01');Now connect to pxc02:
SELECT *FROM dbre_lab.transactions;The row should already exist.
Insert another transaction through pxc02:
INSERT INTO dbre_lab.transactions(description)VALUES ('Inserted through pxc02');Connect to pxc03:
SELECT *FROM dbre_lab.transactions;Both transactions should appear.
This demonstrates one of the fundamental characteristics of PXC: every cluster member maintains the database state.
Important WSREP Metrics
Running:
SHOW STATUS LIKE 'wsrep%';returns a very large number of metrics.
For day-to-day troubleshooting, a smaller group is particularly useful:
SHOW STATUS WHERE Variable_name IN ( 'wsrep_cluster_size', 'wsrep_cluster_status', 'wsrep_connected', 'wsrep_ready', 'wsrep_local_state_comment', 'wsrep_local_recv_queue', 'wsrep_flow_control_paused', 'wsrep_local_cert_failures', 'wsrep_local_bf_aborts');For a healthy three-node cluster we normally expect:
wsrep_cluster_size 3wsrep_cluster_status Primarywsrep_connected ONwsrep_ready ONwsrep_local_state_comment SyncedThese are excellent metrics to include in monitoring and alerting.
Failure Exercise 1: Lose One Node
Now the interesting part begins.
Stop MySQL on pxc03:
sudo systemctl stop mysqlCheck the cluster from another node:
SHOW STATUS LIKE 'wsrep_cluster_size';The cluster should report:
2Because two of the three voting members remain available, quorum still exists.
Writes should continue succeeding:
INSERT INTO dbre_lab.transactions(description)VALUES ('Transaction while PXC03 is unavailable');Bring the node back:
sudo systemctl start mysqlWatch its synchronization process:
journalctl -u mysql -fFailure Exercise 2: Observe an IST
Stop pxc03 again:
sudo systemctl stop mysqlGenerate additional transactions through pxc01:
INSERT INTO dbre_lab.transactions(description)VALUES ('PXC03 is offline');Now restart the third node:
sudo systemctl start mysqlReview the logs:
journalctl -u mysqlSearch for references to:
ISTIf the required write-sets remain available in GCache, Galera should perform an Incremental State Transfer.
The important relationship is:
Donor | | Missing write-sets vJoinerThe returning server receives only the transactions it missed.
Failure Exercise 3: Force an SST
Next we can intentionally make IST impossible.
The idea is:
1. Stop PXC032. Generate large amounts of data3. Overflow the available GCache4. Restart PXC03Because the donor can no longer provide all of the missing write-sets, PXC must perform a full State Snapshot Transfer.
This is useful because SST introduces very different operational concerns.
Monitor:
SST durationCPU utilizationDisk throughputNetwork utilizationDonor performanceJoiner statusApplication latencyThis is the type of scenario DBRE teams should understand before it happens in production.
Failure Exercise 4: Lose Quorum
Start from a healthy cluster:
PXC01 ✓PXC02 ✓PXC03 ✓Stop the second node:
sudo systemctl stop mysqlThe cluster still has quorum.
Now stop the third node:
sudo systemctl stop mysqlOnly pxc01 remains.
Check:
SHOW STATUS LIKE 'wsrep_cluster_status'; SHOW STATUS LIKE 'wsrep_ready';The remaining server should no longer behave like a normal healthy cluster member.
This is an important demonstration of the difference between:
MySQL is runningand:
The database is safe to receive trafficA process being alive does not necessarily mean the database should be included in the application load balancer.
Failure Exercise 5: Create a Network Partition
Database processes are not the only things that fail.
Networks fail too.
Block Galera traffic on one node:
sudo iptables -A INPUT -p tcp --dport 4567 -j DROPsudo iptables -A OUTPUT -p tcp --dport 4567 -j DROPObserve:
SHOW STATUS LIKE 'wsrep%';Pay particular attention to:
wsrep_cluster_sizewsrep_cluster_statuswsrep_connectedwsrep_readyRemove the firewall rules afterward:
sudo iptables -D INPUT -p tcp --dport 4567 -j DROPsudo iptables -D OUTPUT -p tcp --dport 4567 -j DROPThis exercise demonstrates that:
mysqld crashVM crashnetwork partitionslow networkdisk failureare very different failure modes.
A reliable database platform needs to detect and respond appropriately to each of them.
Add HAProxy
Our next improvement is to stop applications from connecting directly to individual database nodes.
The architecture becomes:
Application | v +---------------+ | HAProxy | | 192.168.50.10 | +-------+-------+ | +------------+------------+ | | | v v v +-------+ +-------+ +-------+ | PXC01 | | PXC02 | | PXC03 | +-------+ +-------+ +-------+Install HAProxy:
sudo apt updatesudo apt install -y haproxyOne particularly important consideration is the health check.
Checking only:
TCP port 3306is not sufficient.
For example, a database can have:
mysqld running3306 listeningwhile simultaneously reporting:
wsrep_ready = OFFThat server should not receive application traffic.
A PXC-aware health check should therefore evaluate cluster state before declaring a node healthy.
Monitoring the Cluster
Once the basic architecture works, add monitoring.
Percona Monitoring and Management, or PMM, is a natural choice for a Percona lab.
The environment could eventually look like this:
Application | HAProxy | +------------+------------+ | | | PXC01 PXC02 PXC03 | | | +------------+------------+ | PMMUseful metrics include:
CPU utilizationMemory consumptionDisk latencyBuffer pool utilizationConnectionsQuery latencySlow queriesReplication queuesGalera flow controlCertification failuresSST activityIST activityIn particular, metrics such as:
wsrep_local_recv_queuewsrep_flow_control_pausedwsrep_local_cert_failureswsrep_local_bf_abortscan tell us much more about the health of the cluster than simply verifying whether MySQL is running.
Turning the Lab Into a DBRE Training Environment
Installing Percona is only the beginning.
The real value comes from operating and breaking it.
A useful learning progression would be:
Stage 1 — Build
Deploy three PXC nodes manually.
Understand every configuration parameter instead of immediately automating everything.
Stage 2 — Operate
Add:
HAProxyProxySQLPMMBackupsMonitoringAlertsStage 3 — Break
Simulate:
Node failuresVM failuresNetwork partitionsDisk full conditionsHigh latencyFirewall failuresConfiguration errorsReplication problemsStage 4 — Recover
Practice:
IST recoverySST recoveryQuorum recoveryNode replacementFull-cluster recoveryStage 5 — Maintain
Perform:
Rolling restartsOperating-system maintenanceDatabase upgradesConfiguration changesCertificate rotationwithout taking the entire database offline.
Stage 6 — Automate
After understanding the manual process, rebuild the environment using tools such as:
TerraformAnsibleGitHub ActionsThe objective should be to make the cluster reproducible.
Stage 7 — Observe
Build alerts and dashboards around:
WSREP stateFlow controlCertification conflictsNode synchronizationQuery latencyDisk latencySST and IST eventsStage 8 — Document
Finally, write operational runbooks.
Examples:
PXC node failedCluster is Non-PrimaryNode cannot rejoinSST is stuckDisk is fullFlow control is highRolling restart procedureRolling upgrade procedureComplete cluster outageThis turns a simple home lab into something that closely resembles the operational work performed by Database Reliability Engineering teams.
Full-Cluster Recovery
One scenario deserves special attention:
all cluster nodes are down.
Imagine:
PXC01 XPXC02 XPXC03 XAt this point, we should not simply select a random node and bootstrap the cluster.
Each server may contain a different Galera sequence position depending on which transactions it successfully processed before the outage.
During recovery, administrators need to determine which node contains the most advanced trustworthy state.
A typical recovery investigation includes tools such as:
mysqld --wsrep-recoverThe objective is to determine the Galera position associated with each node and bootstrap from the correct database state.
Choosing the wrong node can potentially mean starting the new cluster from an older state.
That makes full-cluster recovery one of the most valuable exercises to practice in a PXC lab.
What This Lab Teaches
A Percona XtraDB Cluster lab teaches much more than MySQL installation.
It provides hands-on experience with distributed database concepts such as:
QuorumConsensusWrite-set replicationTransaction certificationSplit-brain preventionSynchronous replicationFailure domainsState synchronizationFlow controlAutomated failoverIt also highlights an important reliability engineering principle:
A running database process is not necessarily a healthy database service.
A node may have MySQL running and port 3306 listening while being disconnected from the Primary Component or unable to safely process application traffic.
That distinction is why health checks, observability, quorum awareness, and automated failover are critical parts of database infrastructure.
Conclusion
Percona XtraDB Cluster provides an excellent platform for learning how highly available MySQL environments operate.
A three-node lab gives us a safe environment to explore behaviors that are difficult—or dangerous—to experiment with in production.
Instead of stopping after a successful installation, intentionally break the cluster.
Kill a node.
Disconnect the network.
Force an SST.
Lose quorum.
Fill the disk.
Restart every server.
Recover the cluster.
Then automate everything and repeat the exercises.
The objective is not simply to learn how to install Percona.
The objective is to understand what happens when the database infrastructure stops behaving the way we expect.
That is where the lab starts becoming valuable from a Database Reliability Engineering perspective.
Comments