Percona Operator for MySQL 1.3.0: autopilot tuning, zero-data-loss async failover, encrypted PITR

October 29, 2026
Author
Slava Sarzhan
Share this Post:

 

Percona Operator for MySQL 1.3.0 is out. This release moves three things forward for teams running MySQL on Kubernetes: the operator now tunes MySQL for you based on the pod resources, async clusters can fail over without losing data in flight, and point-in-time recovery works on clusters where binary logs are encrypted at rest.

The headline features in this post are automatic MySQL configuration tuning, zero data loss failover for asynchronous clusters, and encrypted binary logs for point-in-time recovery. Each of them removes a step that teams were doing by hand, or a case they had to accept as a known gap.

As with every operator release, most of what shipped came from what the community asked for on forums.percona.com and in JIRA. The operator is open source and runs on any CNCF-conformant Kubernetes.

In this post, you’ll learn about:

  • Important upgrade notes you should read before you touch 1.3.0
  • Automatic MySQL configuration tuning (and how it hot-applies without a restart)
  • Zero data loss failover for asynchronous clusters
  • Encrypted binary logs for point-in-time recovery
  • Other improvements in the release
 

Before you upgrade

Two defaults changed in 1.3.0. Review both before upgrading.

 

Leader election is on by default

The operator now runs with leader election enabled out of the box. In every realistic deployment this is what you want. If you have a single-replica controller and want the old behavior for a specific reason (local development, debugging), set the PSO_LEADER_ELECTION_ENABLED environment variable on the operator Deployment to false.

 

Asynchronous clusters now require backups

This is the one that will trip an upgrade. In 1.3.0, async clusters must have backups enabled, because the zero data loss failover feature (below) relies on reading binary logs from the failed primary. If backups are not configured on an async cluster, the operator refuses to reconcile.

You have two options:

  1. Enable backups on the cluster (the recommended path). See the backups documentation.
  2. Explicitly opt out of zero data loss failover by setting the escape hatch:

    This restores the pre-1.3.0 behavior: fast failover, but transactions committed on the old primary that had not yet reached the chosen replica are lost.

Group Replication clusters are unaffected by this change.

 

Automatic MySQL configuration tuning

 

Why it matters

Tuning MySQL by hand for a given pod shape is work most teams do once and then forget. When the pod resource limits change, the hand-tuned values rarely follow. 1.3.0 adds a first-class autoConfig block to the CR so the operator computes the MySQL configuration from the pod’s CPU and memory, and the workload shape you declare.

 

How it works

The relevant fields live under spec.mysql.autoConfig. enabled turns the feature on, loadType tells the operator the workload shape, and version tells it which MySQL version to tune for. The accepted loadType values are mostlyReads, someWrites, equalReadsWrites, and heavyWrites. The version string follows the X.Y.Z MySQL version format.

The CRD enforces that mysql.resources sets both CPU and memory when autoConfig.enabled is true. Without those limits, the operator has no basis for its calculation.

 

Dynamic configuration changes

Where a MySQL variable allows it, the operator applies configuration changes with SET GLOBAL instead of a restart. Static variables still require a restart, which the operator rolls through pods one at a time.

 

Wiring it up

The following block comes from the sample CR in the operator repository:

With that block, the operator sizes the buffer pool and friends from the 8 GiB / 4 CPU limits, biases the configuration toward a mixed workload with some writes, and tunes for MySQL 8.4.0. Change the limits or the load type and the operator recomputes.

 

Zero data loss failover for asynchronous clusters

 

Why it matters

Async MySQL replication, by definition, acknowledges a transaction to the client before any replica has it. If the primary dies with transactions in its binary log that no replica has read yet, those transactions are gone, even with a well-configured HA controller.

 

How it works

The operator extends the xtrabackup sidecar on every MySQL pod with a POST /failover/stream endpoint on port 6450 that serves binary logs as a tar stream. When the primary fails, the failover tool runs on the chosen replica.

 

The salvage sequence

  1. Probe the failed primary and stand down if it has come back.
  2. Stop replication and flush relay logs.
  3. Read Source_Log_File and Read_Source_Log_Pos to find the gap.
  4. Fetch the missing binary logs from the failed primary’s sidecar.
  5. Trim the fetched binlogs back to their last complete transaction.
  6. Splice them into the latest closed relay log.
  7. Start the SQL thread and wait until every missing transaction is applied.

Only then does the orchestrator promote the chosen replica.

 

Timeouts and the escape hatch

The failover has a budget. By default, the failover tool waits up to one hour for the source pod to become reachable and apply the missing transactions. When the budget expires, the on-timeout policy decides what happens: Abort holds the cluster, or ForceWithPossibleDataLoss promotes anyway. The budget is counted from the first attempt, not per retry.

Because the mechanism requires the failed primary’s pod to be reachable (its mysqld can be down, but its sidecar must answer), the CRD now validates that backups are enabled on async clusters. To opt out of this gate and restore the pre-1.3.0 behavior, set spec.unsafeFlags.asyncFailoverWithPossibleDataLoss: true.

 

Encrypted binary logs for point-in-time recovery

 

Why it matters

The operator’s binlog server ships binary logs to object storage for point-in-time recovery. In 1.3.0 those shipped files can be encrypted at rest, with the key material held in a Kubernetes Secret instead of being carried on each MySQL pod.

 

How it works

The binlog server uses envelope encryption. A per-file data encryption key (DEK) encrypts each binlog file. That DEK is itself wrapped under a key encryption key (KEK) that the operator reads from a keyring Secret. The CR surfaces three fields for this:

  • spec.backup.pitr.binlogServer.storage.encryption.cipher picks the symmetric cipher (AES-256-CTR by default).
  • spec.backup.pitr.binlogServer.storage.encryption.kekId names the KEK entry inside the keyring.
  • spec.backup.pitr.binlogServer.keyringSecret points at the Kubernetes Secret that holds the keyring file (default key keyring.json).

On the restore side, the PITR tool reads the keyring, finds the KEK named in the file’s metadata, unwraps the file key, and streams the decrypted events through mysqlbinlog into the restored MySQL. If the keyring is missing or the named KEK is not in it, the restore fails fast with an explicit error rather than producing partially restored data.

 

Wiring it up

Enable PITR with an encrypted binlog server. The relevant block in the CR is spec.backup.pitr.binlogServer, which carries its own encryption sub-block and a reference to the keyring secret:

The encryption.cipher and encryption.kekId fields are what tell the binlog server to encrypt shipped binary logs at rest. The keyringSecret provides the master key material.

 

Other improvements

The release notes have the full list with ticket IDs. The highlights:

  • Application users managed in the CR: declare application users in the top-level spec.users list, each with name, optional passwordSecretRef, dbs, hosts, grants, and withGrantOption.
  • Custom CA for S3-compatible storage: the backup path now accepts a custom CA bundle, which lets you point at private MinIO or S3-compatible endpoints behind a corporate certificate authority.
  • cert-manager ClusterIssuer support: the TLS block can reference a cluster-scoped issuer instead of a namespaced one.
  • TLS rotation without a restart: the operator merges old and new CA, hot-reloads leaves, and drops the old root once propagation is confirmed.
  • Backup deadlines: a backup that exceeds its deadline fails fast with a clear status instead of hanging.
  • Configurable leader election: tune the leader-election parameters via PSO_LEADER_ELECTION_* environment variables on the operator Deployment.
  • Persistent operator logging: operator logs survive controller restarts.
  • DigitalOcean Kubernetes (DOKS) support as an officially supported platform.
  • MySQL 9.7 added to the supported-version matrix.
  • Replication type switch (tech preview), ClusterSet rejoin, replication lag reporting, even-sized async clusters, and async bootstrap from backup round out the async and multi-cluster story.
 

Conclusion

Percona Operator for MySQL 1.3.0 tightens the parts of a MySQL platform that reviews and on-call rotations care about most: autoconfig gives each pod the configuration its CPU and memory actually warrant, zero data loss failover salvages the transactions stranded on a failed primary before promoting a replica, and encrypted binlog shipping lets point-in-time recovery stay on, under keys you control, for clusters where encryption at rest is a hard requirement. Application users in the CR, custom CAs for private S3 endpoints, and in-place TLS rotation move more day-two work out of runbooks and into the spec. If there is a workflow you still script around the operator, tell us on the forum, since that is where releases like this one come from.

 

Try It Out

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