Enabling TLS in PXC without Downtime

September 9, 2026
Author
Juan Arruti
Share this Post:

Starting with Percona XtraDB Cluster (PXC) 8.0, replication traffic encryption is enabled by default. That said, it’s common to find clusters running without TLS that suddenly need it: a new compliance requirement, an audit finding, a network segment that is no longer considered trusted.

PXC has a variable for exactly that case, pxc-encrypt-cluster-traffic, which handles SSL encryption for inter-node traffic, including State Snapshot Transfer (SST), Incremental State Transfer (IST), and the group communication the nodes use for replication.

The variable is not dynamic, and turning it on normally costs a full cluster restart since the node that encrypts traffic listens on ssl:// while its peers are still on tcp://. If a node joins the cluster with TLS enabled while remaining nodes don’t, the restarting node fails to reach out to the other peers with the following error:

This is a common OpenSSL error seen when a client sends encrypted TLS traffic to a server that expects unencrypted plaintext.

Starting with PXC 8.0.28 and in all PXC 8.4 versions, there is a way to work around the full cluster restart, which relies on the Galera socket.dynamic option. In this post I’ll go through how this option works and how it can be used to enable TLS without downtime.

The socket.dynamic Option

This provider option lets a node establish communication with other peers using both encrypted and plaintext connections. The instance will initially open an encrypted connection, and if it fails since the other nodes are not using TLS, it will retry to establish an unencrypted connection.

The Joiner error log will show the following sequence:

The first line is the same error we saw previously: our node initiated a TLS handshake, but the peer responded with something that was not TLS. Without socket.dynamic, the node would stop at that point and never join, but with it, it retries (without encryption) and is able to establish a connection.

This Galera option can’t be changed at runtime, so you need to restart the node to enable it.

The Procedure

In this procedure, we consider a typical PXC architecture with 3 nodes. Enabling encryption requires two rolling restarts: the first turns TLS on while still allowing plaintext connections, and the second removes that option and forces only TLS connections.

  1. Distribute the same certificate files across all nodes:

Whether the certificates are auto-generated by MySQL or issued by us, every member of the cluster has to use the same key and certificate files. It’s recommended to keep the certificates outside the data directory for security and configuration clarity, and to keep them clear of SST since a joiner wipes its data directory before a full state transfer, and only files matching the [sst] cpat pattern survive. That default covers *.pem, so the MySQL-generated names are safe, but a .crt or .key left in the data directory is deleted.

  1. Update the my.cnf on every node:

– pxc-encrypt-cluster-traffic=ON, this switch adds TLS for group communication, IST, and SST.

– socket.dynamic=YES option inside the wsrep_provider_options variable, so each node can speak both protocols while the rollout is in progress.

– The ssl-* variables, in case we’re setting the certificates outside the default path, which is the data directory.

– An [sst] section with the encrypt=4 option and the ssl-* variables. Since pxc-encrypt-cluster-traffic enforces SST channel to encrypt=4, this is a safety measure for those that did not yet performed the change, so donor and joiner can communicate over TLS while the change has not been applied in all nodes. Even though nothing has been restarted, the SST script re-reads my.cnf on every transfer, so as soon as the file is in place the donors encrypt SST whether or not they have been restarted.

The my.cnf file shows the following values:

  1. Restart node #1 and node #2, one at a time:

Wait for each node to rejoin and report Synced before moving to the next. A restarted node will try TLS first and then fall back.

You can verify the change was correctly applied by running the following query. Since the SSL settings are in the wsrep_provider_options variable, which is a long string of key/value pairs, we pull out only the ones we are interested in:

The provider option socket.ssl and ssl certificates are passed down from MySQL to the provider because pxc-encrypt-cluster-traffic is on.

That confirms the settings reached the provider. That said, the replication traffic is not encrypted yet.

We can check this by using tcpdump on node #1 to listen on the replication port (4567) packets coming from node #2:

On node #2 we create the table_test that will be replicated to the other nodes:

Since in PXC DDLs are replicated as a statement, tcpdump on node #1 shows the create table command in plaintext:

  1. Restart the third node with socket.dynamic=NO:

Node #3 does not need the socket.dynamic option since the other two nodes are already capable of talking either TLS or plaintext.

We modify the my.cnf option file as below:

And restart the node. This node will start and request only TLS from the other nodes, which they can respond to, since they already have all the changes in place.

The node will show reaching out to cluster peers over ssl://

  1. Restart node #1 and node #2, turning off the socket.dynamic option:

In order to disallow the non-encrypted option and to establish a TLS connection with the other nodes, nodes #1 and #2 should be restarted.

Similar to the previous step, when restarting, the nodes will establish communication on TLS.

Node #1

Node #2

To confirm the replication traffic is encrypted, we can perform the same check as in step 3, by running tcpdump on node #1 and listening on the replication port (4567) for packets coming from node #2:

On node #2 we drop the table_test previously created:

tcpdump does not show a matching pattern since the replication traffic is now encrypted.

Disabling TLS

We can use the same mechanism to disable TLS for whatever reason. As with enabling it, this takes two rolling restarts. Starting from a cluster where every node has pxc-encrypt-cluster-traffic=ON we perform the following steps:

– Restart nodes #1 and #2 with socket.dynamic=YES, keeping pxc-encrypt-cluster-traffic=ON, so they accept plaintext connections again. 

– Restart node #3 with pxc-encrypt-cluster-traffic=OFF, it will come up in plaintext only, and the other two nodes will accept it because they are back to accepting both protocols.

– Restart nodes #1 and #2 with pxc-encrypt-cluster-traffic=OFF and without socket.dynamic, which leaves the whole cluster in plaintext.

– Finally, remove from the sst section the encrypt and SSL related variables.

Conclusion

Enabling TLS for inter-node traffic can be done without stopping the whole cluster. The socket.dynamic option lets a node accept both encrypted and plaintext connections during the transition, so the change can be rolled out one node at a time. Please note we’ll need at least two rolling restarts to achieve that the cluster fully communicates via TLS.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted

Far
Enough.

Said no pioneer ever.
MySQL, PostgreSQL, InnoDB, MariaDB, MongoDB and Kubernetes are trademarks for their respective owners.
© 2026 Percona All Rights Reserved