Key Management Overview

pg_vault_tde separates two concerns: the DEK (Data Encryption Key — one per table, encrypts the actual rows) and the KEK (Key Encryption Key — protects the DEKs at rest). The DEK is never stored unencrypted on disk; only the wrapped (KEK-encrypted) DEK is stored, in the pg_vault_tde_catalog table. Where the KEK itself lives is what a KMS provider decides.

The Three KMS Providers

Provider kms_provider value KEK lives in Network dependency Best for
HashiCorp Vault / OpenBao vault Vault/OpenBao Transit engine Yes — HTTP(S) to Vault Centralized key management, existing Vault/OpenBao infrastructure, multi-server clusters
Local Wallet local Encrypted PKCS#12 file on local disk No Standalone servers, air-gapped/offline environments, development
PKCS#11 / HSM pkcs11 Hardware security module (Thales, Utimaco, YubiHSM, AWS CloudHSM, SoftHSM2 for testing) Depends on HSM (local device or network HSM) Regulatory/compliance requirements for hardware key custody

There is no default providerkms_provider defaults to an empty string, which the extension treats as “not yet configured.” Set it explicitly before creating any encrypted table:

pg_vault_tde.kms_provider = 'vault'
# pg_vault_tde.kms_provider = 'local'
# pg_vault_tde.kms_provider = 'pkcs11'

kmip (KMIP 1.2) is reserved for a future release (v1.8) and is not implemented yet.

Key Hierarchy

KMS provider (Vault Transit / local wallet / HSM)
        │  owns/holds the KEK
        │
        ▼  wrap / unwrap (AES-256-WRAP or provider-native)
pg_vault_tde_catalog (on-disk table)
        │  one row per relation: wrapped_dek, kms_provider, generation, timestamps
        │
        ▼  unwrap on cache miss
Shared-memory DEK cache (TdeRelDekMap)
        │  one entry per relation; single LWLock; per-relation generation counter
        │
        ▼
AES-256-GCM tuple encryption / AES-256-SIV index-key encryption
  • The DEK is generated automatically the first time a table is created USING encrypted_heap — there is no manual DEK provisioning step.
  • The KEK is provisioned once per provider (Vault Transit key, wallet passphrase + generated key, or HSM keygen) and afterward is normally never handled directly by an operator.
  • An attacker who obtains a filesystem or pg_basebackup copy of the data directory only ever sees wrapped DEKs — the KEK itself never travels with the data (see Backup and Restore).

Per-Database KMS Configuration

All KMS-related GUC parameters (kms_provider, vault_*, wallet_*, pkcs11_*) have context suset: a superuser can set them per-database with ALTER DATABASE ... SET, and the change takes effect for new connections without a server restart. This is the primary mechanism for multi-tenant key isolation on a shared cluster:

-- cluster-wide default (postgresql.conf / ALTER SYSTEM)
-- pg_vault_tde.kms_provider = 'vault'

-- tenant_a: dedicated Vault Transit key
ALTER DATABASE tenant_a SET pg_vault_tde.vault_key_name      = 'tde-dek-tenant-a';
ALTER DATABASE tenant_a SET pg_vault_tde.vault_transit_mount = 'transit-tenants';

-- tenant_b: fully offline, no Vault dependency at all
ALTER DATABASE tenant_b SET pg_vault_tde.kms_provider          = 'local';
ALTER DATABASE tenant_b SET pg_vault_tde.wallet_passphrase_env = 'TDE_WALLET_B';

-- verify effective configuration after reconnecting
\connect tenant_b
SHOW pg_vault_tde.kms_provider;        -- 'local'
SELECT * FROM pg_vault_tde_health_check();

The only parameters that are not database-scoped (postmaster context, require a full restart) are pg_vault_tde.enabled, max_encrypted_relations, and crypto_provider — these affect shared-memory sizing or the master crypto switch and are cluster-wide by nature. See GUC Reference for the context of every parameter.

DEK Caching (Shared Memory)

Every encrypt/decrypt call looks up the relation’s DEK in a shared-memory hash table (TdeRelDekMap, capacity controlled by max_encrypted_relations, default 1024, restart required to change). A cache hit is an O(1) shared-lock lookup; a cache miss reads the wrapped DEK from pg_vault_tde_catalog and unwraps it through the active KMS provider (one network round-trip for Vault, one local unwrap for the wallet or HSM), then inserts it into the cache. This is why the first query against a newly-created table, or the first query after a fresh connection, pays a small extra latency cost, and subsequent queries do not.

Rotation

pg_vault_tde distinguishes DEK rotation (re-encrypts table data with a new per-table key) from KEK rotation (re-wraps the existing DEKs under a new master key, without touching a single row of table data). Both are online operations with no exclusive table lock. See Key Rotation for the full runbook, and each provider-specific page for provider-specific rotation details (e.g. PKCS#11 KEK generations that never expire from the token).

See Also