Transparent Data Encryption (TDE) encrypts all user data at the database layer — data files, write-ahead logs (WAL), and the key management file on shared storage. Authenticated users access data transparently without any changes to application code or configurations. Unauthorized users who bypass the database and access the storage layer directly see only encrypted bytes.
TDE helps you meet encryption-at-rest requirements for the following compliance standards:
China: Cryptography Law of the People's Republic of China (effective January 1, 2020), Classified Protection of Cybersecurity (GB/T 22239-2019)
International: Payment Card Industry Data Security Standard (PCI DSS), Health Insurance Portability and Accountability Act (HIPAA), General Data Protection Regulation (GDPR), California Consumer Protection Act (CCPA), Sarbanes-Oxley Act (SOX)
Supported versions
TDE is available on PolarDB for PostgreSQL clusters running the following engine versions:
| Engine version | Minimum minor version |
|---|---|
| PostgreSQL 11 | 2.0.11.2.1.0 |
| PostgreSQL 14 | 2.0.14.5.1.1 |
| PostgreSQL 16 | 2.0.16.9.6.0 |
| PostgreSQL 17 | 2.0.17.6.4.0 |
| PostgreSQL 18 | 2.0.18.0.1.0 |
TDE is not supported on PolarDB for PostgreSQL Distributed Edition clusters.
To check your minor engine version, run SHOW polardb_version; or view it in the console. If your version does not meet the requirement, upgrade the minor engine version.
Limitations
TDE is not supported on PolarDB for PostgreSQL Distributed Edition clusters.
Key concepts
| Term | Description |
|---|---|
| Key Encryption Key (KEK) | A key that encrypts another key. The KEK is derived from an external passphrase command and is never stored on disk. |
| Memory Data Encryption Key (MDEK) | The root data encryption key, randomly generated by pg_strong_random and kept only in memory. |
| Table Data Encryption Key (TDEK) | Derived from the MDEK using the HMAC-based Key Derivation Function (HKDF). Encrypts table data. |
| WAL Data Encryption Key (WDEK) | Derived from the MDEK using HKDF. Encrypts write-ahead log data. |
| Hash-based Message Authentication Code (HMAC) | A keyed-hash algorithm used for integrity verification. In TDE, a passphrase is processed with SHA-512 to produce 64 bytes: the first 32 bytes become the KEK, the last 32 bytes become the HMACK. |
| Encode Memory Data Encryption Key (ENCMDEK) | An encrypted data encryption key that is stored in memory. Generated by encrypting the MDEK with the KEK. |
| KEK_HMAC | A digest generated from the ENCMDEK and HMACK using the HMAC algorithm. Stored alongside the ENCMDEK and used as verification information when a key is reverted. |
How it works
TDE operates through three coordinated modules: key management, encryption, and decryption.
Key management
Key structure
TDE uses a two-layer key hierarchy:
Top layer — KEK: Protects the data encryption key. Retrieved externally on each startup via the
polar_cluster_passphrase_commandparameter (for example, from a Key Management Service (KMS) or, for testing,echo passphrase).Bottom layer — data encryption keys (MDEK, TDEK, WDEK): Perform the actual encryption of data pages and WAL records.
The ENCMDEK and KEK_HMAC are saved on shared storage in the global/kmgr file (KmgrFileData structure), so every node in the cluster — primary and read-only — can recover the data encryption keys at startup. This file is created during database initialization (initdb), which means standby nodes can obtain it via pg_basebackup.
When the cluster is running, key material is held in process memory:
static keydata_t keyEncKey[TDE_KEK_SIZE];
static keydata_t relEncKey[TDE_MAX_DEK_SIZE];
static keydata_t walEncKey[TDE_MAX_DEK_SIZE];
char *polar_cluster_passphrase_command = NULL;
extern int data_encryption_cipher;Key initialization
Keys are generated once during database initialization:

Run
polar_cluster_passphrase_commandto get the 32-byte KEK and 32-byte HMACK.Call the OpenSSL random number generator to produce the MDEK.
Derive the TDEK from the MDEK using HKDF (OpenSSL).
Derive the WDEK from the MDEK using HKDF (OpenSSL).
Encrypt the MDEK with the KEK to produce the ENCMDEK.
Generate the KEK_HMAC from the ENCMDEK and HMACK using the HMAC algorithm.
Write the ENCMDEK, KEK_HMAC, and other
KmgrFileDatafields to theglobal/kmgrfile.
The KmgrFileData structure on disk:
typedef struct KmgrFileData
{
/* version for kmgr file */
uint32 kmgr_version_no;
/* Are data pages encrypted? Zero if encryption is disabled */
uint32 data_encryption_cipher;
/*
* Wrapped Key information for data encryption.
*/
WrappedEncKeyWithHmac tde_rdek;
WrappedEncKeyWithHmac tde_wdek;
/* CRC of all above ... MUST BE LAST! */
pg_crc32c crc;
} KmgrFileData;Key decryption (on restart)
When the database restarts or recovers from a crash, TDE reconstructs the data encryption keys from the stored ciphertext:

Read the
global/kmgrfile to get the ENCMDEK and KEK_HMAC.Run
polar_cluster_passphrase_commandto get the KEK and HMACK.Generate KEK_HMAC' from the ENCMDEK and HMACK using HMAC. If it matches the stored KEK_HMAC, proceed. If not, return an error.
Decrypt the ENCMDEK with the KEK to recover the MDEK.
Derive the TDEK from the MDEK using HKDF (deterministic — same MDEK always produces the same TDEK).
Derive the WDEK from the MDEK using HKDF (deterministic — same MDEK always produces the same WDEK).
Key rotation
Key rotation re-encrypts the MDEK under a new KEK without changing the MDEK itself, so all existing data remains readable after rotation. The process updates the global/kmgr file with the new ENCMDEK and KEK_HMAC.

Read the
global/kmgrfile to get the ENCMDEK and KEK_HMAC.Run
polar_cluster_passphrase_commandto get the current KEK and HMACK (64 bytes total).Generate KEK_HMAC' from the ENCMDEK and HMACK using HMAC. If it matches the stored KEK_HMAC, proceed. If not, return an error.
Decrypt the ENCMDEK with the KEK to recover the MDEK.
Run
polar_cluster_passphrase_commandagain to get the new KEK (new_KEK) and new HMACK (new_HMACK).Encrypt the MDEK with new_KEK to produce new_ENCMDEK.
Generate new_KEK_HMAC from new_ENCMDEK and new_HMACK using HMAC.
Write new_ENCMDEK, new_KEK_HMAC, and other
KmgrFileDatafields to theglobal/kmgrfile.
Encryption
All user data is encrypted at the page level using AES-128 or AES-256. AES-256 is used by default.
Each data page is encrypted with a unique initialization vector (IV) derived from the (page LSN, page number) pair. This ensures that encrypting the same plaintext at different times produces different ciphertext.
The page header structure:
typedef struct PageHeaderData
{
/* XXX LSN is member of *any* block, not only page-organized ones */
PageXLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog
* record for last change to this page */
uint16 pd_checksum; /* checksum */
uint16 pd_flags; /* flag bits, see below */
LocationIndex pd_lower; /* offset to start of free space */
LocationIndex pd_upper; /* offset to end of free space */
LocationIndex pd_special; /* offset to start of special space */
uint16 pd_pagesize_version;
TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;Three fields in each page header are intentionally left unencrypted:
| Field | Reason |
|---|---|
pd_lsn | Required as the IV input for encryption and decryption |
pd_flags | The 0x8000 bit indicates whether the page is encrypted; leaving it unencrypted allows backward-compatible reading of plaintext pages and supports enabling TDE on existing clusters |
pd_checksum | Checksum is verified against the ciphertext, so it must remain readable before decryption |
Encrypted file locations
All files that contain user data are encrypted. These include files in the following subdirectories of the data directory:
base/global/pg_tblspc/pg_replslot/pg_stat/pg_stat_tmp/
Encryption timing
Encryption happens immediately before the checksum is calculated, just before a page is written to disk. Even when checksums are disabled, PageSetChecksumCopy or PageSetChecksumInplace is always called — TDE hooks into this step to ensure every byte written to storage is encrypted.
Decryption
When a page is read from storage into memory, decryption runs immediately after the checksum is verified. Even when checksums are disabled, PageIsVerified is always called — TDE hooks into this step to ensure the data in memory is always in plaintext.