
Percona Operator for PostgreSQL 3.1.0 takes on three things that decide whether a PostgreSQL platform passes review: is the data encrypted at rest, can it serve reads without straining the primary, and are the logs there when you need them. This release answers all three inside the custom resource, so none of them is a bolt-on you maintain yourself.
The three headline features are transparent data encryption with pg_tde, logical replicas, and persistent logging. pg_tde encrypts your data on disk, including the write-ahead log. Logical replicas add a read-only copy inside the cluster for reporting and analytics. Persistent logging keeps PostgreSQL and pgBackRest logs across Pod restarts.
The operator is open source and runs on any CNCF-certified Kubernetes distribution. This release also widens where it runs, adding official Rancher Kubernetes Engine (RKE2) support and full ARM64 images. Much of what shipped here comes from requests on forums.percona.com and the public issue tracker.
In this post, you’ll learn about:
Encryption at rest is usually the line item that blocks a database from going into a regulated environment. Storage-level encryption from the cloud provider covers the disk, but it does not protect a copied volume, a leaked backup, or a stray WAL segment, and auditors increasingly want encryption the database itself controls. This release adds transparent data encryption through pg_tde, Percona’s open source TDE extension for PostgreSQL.
With pg_tde, the data in tables, indexes, temporary tables, and the write-ahead log stays encrypted on disk, and PostgreSQL decrypts it only in memory for a session that holds the key. That closes the gaps storage encryption leaves open: a snapshot of the volume, a backup shipped to object storage, or a WAL file replicated off-node is ciphertext without the key. Because the key lives in an external provider rather than next to the data, you separate who runs the database from who controls the keys. In practice this is what lets a team bring PostgreSQL on Kubernetes into scope for a standard like PCI DSS or HIPAA without moving the workload off the platform: the encryption the auditor asks about lives in the database, and key custody lives in Vault under a different team’s control.
The operator wires pg_tde to HashiCorp Vault as the key provider. You enable the extension in the custom resource and point it at your Vault instance and a token Secret. The operator handles loading the extension and configuring the key set on the PostgreSQL instances. WAL encryption is a separate switch, so you can encrypt table data and the write-ahead log together. pg_tde uses a two-tier key model: a principal key in Vault wraps the internal data keys, so rotating the principal key is a key-management operation in Vault rather than a full re-encryption of the database.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster metadata: name: cluster1 spec: extensions: pg_tde: enabled: true walEncryption: true vault: host: https://vault-service:8200 mountPath: tde tokenSecret: name: pg-tde-vault-secret key: token caSecret: name: pg-tde-vault-secret key: ca.crt |
enabled: true loads pg_tde and turns on encryption for the cluster, and walEncryption: true extends it to the write-ahead log. The vault block points at your key provider: host and mountPath locate the secrets engine, tokenSecret holds the Vault token, and caSecret carries the CA certificate so the operator trusts the Vault endpoint. Keep the Vault token and CA in Kubernetes Secrets, not in the manifest.
Note: pg_tde in 3.1.0 is available for PostgreSQL 17 and 18. Encryption applies to data written after you enable it, so plan enablement as part of provisioning a cluster rather than as a switch on a full production database.

A PostgreSQL cluster under the operator is a primary with streaming physical replicas that Patroni manages for high availability. Those replicas exist to take over on failover, not to be a stable place to point a reporting tool, because their role can change at any time. Teams that want a durable read endpoint for analytics have had to run a second cluster or wire up replication by hand. This release adds a logical replica you declare inside the same cluster.
Reporting and analytics queries have a different shape than transactional traffic: they scan more, run longer, and arrive in bursts when a dashboard refreshes or a nightly job starts. Pointed at the primary, they compete with the writes that keep the application responsive. A logical replica gives that traffic its own copy and its own compute, so a heavy analytics query slows down a chart, not a checkout. Because the endpoint is stable, you set the reporting tool’s connection string once and leave it.
A logical replica is a read-only copy with its own volume and its own Service, seeded from a pgBackRest backup and kept current through logical replication. Patroni does not manage it, so it never gets promoted and its endpoint stays stable: a reporting query or a dashboard can point at it and stay pointed at it. You can target specific databases or replicate all of them, and size the replica independently of the primary.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
spec: logicalReplicas: - name: analytics databases: [] # empty = all non-template databases except "postgres" bootstrapMethod: pgbackrest dataVolumeClaimSpec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi resources: limits: cpu: 2.0 memory: 4Gi expose: type: LoadBalancer |
name becomes the replica’s identity and the basis for its Service. databases selects what to replicate, where an empty list means every non-template database except postgres. bootstrapMethod: pgbackrest seeds the replica from a backup rather than from the live primary, which keeps the initial sync off the primary’s back. dataVolumeClaimSpec and resources size it for the read workload, and expose publishes the read endpoint.
Note: Logical replicas are a tech preview in 3.1.0 and require PostgreSQL 17 or later. Logical replication does not copy schema changes automatically, so treat DDL on the primary as something you coordinate with the replica.
Logs matter most right after something goes wrong, which is exactly when a Pod is most likely to have restarted and taken its logs with it. When PostgreSQL logs only to a container’s stdout, a crash-loop or a reschedule erases the evidence you need to explain it. The classic case is a crash-looping instance: by the time you exec into a fresh Pod, the stdout from the crash is gone, but an on-disk log still holds the panic and the queries around it. This release keeps PostgreSQL and pgBackRest logs on the instance data volume, so they survive Pod restarts.
The operator runs a Fluent Bit log collector as a sidecar that reads the on-disk logs and emits them as structured JSON lines. Because the logs live on the data volume, they persist across restarts and rescheduling. From there you can forward them off-cluster: the collector can ship to S3 or over OpenTelemetry to whatever aggregation stack you already run, configured through the custom resource. The same on-disk logs that help you debug an incident become the audit trail your security team retains, without a second logging agent to install.
|
1 2 3 4 5 6 7 8 9 10 11 |
spec: logcollector: enabled: true image: docker.io/perconalab/fluentbit:main-logcollector # configuration: | # pipeline: # filters: # - name: record_modifier # match: "*" # record: # - cluster_name cluster1 |
enabled: true turns on the Fluent Bit collector, and image pins the collector build. The commented configuration block is a Fluent Bit pipeline you can supply to filter, enrich, or route logs: the example tags every record with a cluster_name, and the same mechanism adds an output to forward logs to S3 or an OpenTelemetry endpoint. You can also tune log rotation so retention matches your operational windows and compliance rules.
Note: Persistent logs consume space on the instance data volume. Set a rotation policy that fits the volume so logs do not compete with the database for storage.
Beyond the three headline features, 3.1.0 ships a set of enhancements that smooth day-two operations:
One deprecation to plan for: this release removes PMM2 support (K8SPG-944), so move monitoring to PMM3. The extensions.builtin field is deprecated in favor of extensions.<name>.enabled; migrate before 3.4.0.
Percona Operator for PostgreSQL 3.1.0 tightens the parts of a PostgreSQL platform that reviews and on-call rotations care about most: pg_tde encrypts data at rest under keys you control, logical replicas give analytics a stable read endpoint without a second cluster, and persistent logging keeps the evidence when a Pod restarts. With RKE2 and full ARM64 support, more of that runs on the platforms teams actually use. 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.
Resources
RELATED POSTS