For the better part of a decade, the answer to “does PostgreSQL support Transparent Data Encryption?” has been: not in the core, but EnterpriseDB has it, and Crunchy has a different one, and Cybertec has yet another, and you can roll your own with filesystem-level encryption if you do not mind that your DBA’s threat model and your auditor’s threat model now disagree.
Percona has now shipped pg_tde, an open-source extension that does block-level encryption of PostgreSQL data files. It is real, it is in production-ready form (currently 2.1.2), and it is licensed permissively enough to actually deploy. The headline performance number is under 10% overhead, which matches what the proprietary implementations have claimed for years.
This piece is aimed at technical management: what pg_tde is, what it actually protects against, what it does not protect against, and whether your organization should be paying attention to it.
What TDE actually means
Transparent Data Encryption protects data at rest. The phrase “at rest” is doing a lot of work. It means: bytes that have been written to disk. It does not mean bytes in memory. It does not mean bytes in transit. It does not mean rows visible to a logged-in user. TDE is the defense against someone who walks off with a backup tape, a decommissioned disk, or a stolen laptop that was running a development copy of your database.
I have been known to state categorically that “TDE is useless.” That is overstatement for dramatic purposes, but the number of threat models that TDE is the solution for are very limited. Most data breaches are via the application layer, not the physical media, and TDE does nothing to solve those.
TDE does nothing to stop a SQL injection attack. It does nothing to stop a compromised application credential. It does nothing to stop a malicious DBA. The encryption key is held by the running database, and the running database decrypts data for anyone who can authenticate.
This is not a deficiency of pg_tde. It is the definition of TDE. But it is the thing to be careful about when your compliance team starts asking what’s encrypted, because the honest answer is “the disk files, and only the disk files.”
What pg_tde encrypts
pg_tde uses AES — 128 or 256 bit, CBC for heap data, CTR for WAL — and a two-layer key hierarchy. Internal keys encrypt the actual data; they live in $PGDATA/pg_tde. Principal keys encrypt the internal keys; they live in an external Key Management System. The supported KMS targets are HashiCorp Vault, OpenBao, and any KMIP-compliant server. There is one principal key per database, and each file with a different OID gets its own internal key.
The extension encrypts:
- Heap tables you explicitly mark as encrypted (it is selective by default; you opt in per-table or globally).
- Indexes on those tables.
- TOAST tables associated with those tables.
- Sequences associated with those tables.
- The WAL stream (this can be enabled separately and is the more recent addition).
This is the right set of things for the threat model. It is also explicitly not the whole set of things, and the gaps matter.
What pg_tde does not encrypt
This is the section to read carefully.
System catalogs are not encrypted. pg_class, pg_attribute, pg_proc, the lot. If an attacker can read your data directory but cannot get the principal key, they can still see your table and column names, your function definitions, your view definitions, and your statistics. For most compliance frameworks this is acceptable — the framework cares about row-level data — but if your schema contains sensitive information (you have a table called oncology_patient_outcomes_q3, say), that information is on disk in cleartext.
Temporary files are not encrypted. Queries that exceed work_mem spill to disk. If your workload includes large sorts, large hash joins, or large CTEs, you will routinely produce on-disk temp files containing the same data your encrypted heap holds. After a crash these files can persist. This is a real gap.
pg_upgrade is not supported. This is the single biggest operational warning. If you encrypt a 14 cluster with pg_tde and try to pg_upgrade to 18, you will corrupt the encryption metadata in the target cluster. The supported upgrade path is logical replication or pg_dump/pg_restore, both of which are dramatically more expensive than pg_upgrade --link for a large database. Factor this into your TCO calculation.
Logical replication does not preserve encryption state. A logical replica of an encrypted table is not encrypted on the subscriber side unless you configure that side separately. Replication-based DR therefore needs pg_tde configured on both ends.
KMS configuration cannot be updated in place. If your principal key provider needs to change — Vault to KMIP, address change, whatever — you do not currently have a clean migration path.
The “do you need this” question
For most PostgreSQL workloads the honest answer is no. Filesystem-level encryption — LUKS on Linux, BitLocker on Windows, encrypted EBS volumes on AWS — covers the same threat model with simpler operational mechanics and no database-level overhead. If you have a checkbox on a compliance form that says “data at rest is encrypted,” filesystem encryption satisfies it for almost all auditors.
The cases where pg_tde is the right tool, rather than filesystem encryption, are narrower than the marketing suggests:
You have a regulatory or contractual requirement that the database, specifically, must encrypt at rest — some financial and healthcare frameworks word their requirements this way, and “the filesystem encrypts it” is not accepted as compliant. PCI-DSS in particular has language that some auditors interpret strictly.
You need per-table or per-database encryption granularity — for example, a multi-tenant system where some tenants have a contractual right to dedicated encryption keys and others do not. Filesystem encryption is all-or-nothing; pg_tde is selective.
You need encryption keys that are not in the same trust domain as the OS — i.e., the OS root user can read the data directory, but the encryption keys live in Vault behind a separate authentication boundary. This is the most defensible argument for TDE over filesystem encryption, and it is the one that matters most for shops with adversarial OS-level threat models.
If none of those three apply, save yourself the operational overhead and use filesystem encryption.
If one of them does apply, pg_tde is now a viable open-source option, and that is a meaningful change in the landscape. The proprietary TDE offerings are still more polished, still better integrated with their respective vendor ecosystems, and still come with a phone number you can call at 2am. But you can now have TDE without taking a vendor dependency, and for a substantial number of organizations that is exactly the trade they want to make.
The recommendation
Do not deploy pg_tde because you read about it and it sounded like good practice. Deploy it because you have a specific written requirement that filesystem encryption does not satisfy.
If you do deploy it: bring pg_upgrade out of your operational playbook, budget for logical replication during major-version upgrades, configure KMS deliberately and document it, and audit your work_mem-spill behavior because temp files are the easiest way to lose the encryption guarantee accidentally.
And if your auditor accepts filesystem encryption, accept that gift and move on to the next problem.