New to KubeDB? Please start with the KubeDB documentation.

Postgres Transparent Data Encryption (TDE)

Transparent Data Encryption (TDE) encrypts a Postgres cluster’s data at rest, so the files on disk (table data, indexes, and optionally the WAL) are unreadable without the encryption keys, even if someone gains access to the underlying storage. KubeDB implements TDE using the Percona pg_tde extension.

TDE is complementary to TLS/SSL, which protects data in transit. You can enable both.

Prerequisite: a Percona distribution version

pg_tde and its tde_heap access method are only available on Percona Server for PostgreSQL 17.x, a patched build. TDE cannot be enabled on a community PostgreSQL image. Use a PostgresVersion whose spec.distribution is Percona and whose spec.tde.supported is true:

$ kubectl get postgresversion 17.9-percona -o jsonpath='{.spec.distribution} {.spec.tde.supported}{"\n"}'
Percona true

The admission webhook rejects spec.tde on any version that does not advertise TDE support.

How it works

pg_tde uses a two-tier key hierarchy:

  • Principal key (one per database): generated by pg_tde and stored in an external Key Management System (KMS). It never lives in the data directory.
  • Internal keys (one per relation file): stored encrypted, wrapped by the principal key, inside $PGDATA/pg_tde/. These encrypt the actual data files.

KubeDB’s Postgres operator wires everything up for you: it registers the key provider, sets the principal key on first boot, mounts the KMS credentials into the pod (outside the data directory), and injects pg_tde into shared_preload_libraries. Replicas are seeded with pg_tde_basebackup and failback uses pg_tde_rewind, so encryption is transparent across HA and failover.

Key providers

You configure exactly one key provider under spec.tde.keyProvider:

ProviderFieldNotes
HashiCorp VaultkeyProvider.vaultRecommended for HA and DR. A global provider.
KMIP serverkeyProvider.kmipFor appliance-based KMS (Thales, Fortanix, etc.). A global provider.
Local keyring filekeyProvider.fileStandalone only. Cannot back replication or WAL encryption.

A replicated cluster (replicas > 1) or one using WAL encryption must use a global provider (Vault or KMIP), because the standbys need to reach the same key material. The webhook enforces this.

Spec fields

spec:
  tde:
    keyProvider:
      vault:
        address: https://vault.example.com:8200
        mountPath: secret
        tokenSecretRef:
          name: vault-token           # Secret key: token
        caSecretRef:                  # optional, Secret key: ca.crt
          name: vault-ca
    encryptWAL: false                 # cluster-wide WAL encryption (needs a rolling restart to enable)
    enforceEncryption: false          # refuse creation of any unencrypted table
    defaultEncryptedTables: false     # make tde_heap the default table access method
    cipher: aes_128                   # aes_128 (default) or aes_256

spec.tde.keyProvider and spec.tde.cipher are immutable after the database is created: changing where the principal key lives, or the cipher, would orphan the keys already used to encrypt existing data. spec.tde.encryptWAL is reconfigured through the EnableWALEncryption PostgresOpsRequest, not by patching the field directly, since enabling it also requires setting up a server key and a rolling restart. Rotating the principal key is likewise done through a RotatePrincipalKey PostgresOpsRequest (see the guide).

Encrypting tables

With TDE enabled you can encrypt tables in three ways:

  • Per table at creation: CREATE TABLE t (...) USING tde_heap;
  • Convert an existing table: ALTER TABLE t SET ACCESS METHOD tde_heap;
  • Cluster-wide: set spec.tde.defaultEncryptedTables: true so every new table is encrypted (default_table_access_method = tde_heap).

Set spec.tde.enforceEncryption: true to block the creation of any unencrypted table.

Limitations

  • Requires Percona Server for PostgreSQL 17.x. Community PostgreSQL is not supported.
  • System catalogs, server statistics, and temporary files spilled to disk (queries exceeding work_mem) are not encrypted.
  • pg_tde is incompatible with the Citus and TimescaleDB extensions.
  • Enabling TDE is not retroactive: existing tables stay unencrypted until you convert them with ALTER TABLE ... SET ACCESS METHOD tde_heap.
  • Losing the principal key means losing the data. The KMS must be reachable at restore time.

Next Steps