Skip to content

Vault configuration

You can configure pg_tde to use HashiCorp Vault as a global key provider for managing encryption keys securely. Both the open source and enterprise editions are supported.

Configure Vault with pg_tde in Docker

This example setup describes how to run HashiCorp Vault and Percona PostgreSQL with pg_tde side by side using Docker, configure Vault policies, and connect it as a global key provider for TDE encryption.

Note

For production deployments, follow your organization’s security standards.

1. Create the docker-compose.yaml file

The following docker-compose.yaml file is an example on how to run both Vault and PostgreSQL with a shared secrets volume:

docker-compose.yaml (example file)
version: '3.8'

services:
vault:
    image: hashicorp/vault:latest
    container_name: vault
    ports:
    - "8200:8200"
    volumes:
    - ./vault-config.hcl:/vault/config/vault-config.hcl
    - ./vault-data:/vault/data
    - shared-secrets:/vault/secrets
    environment:
    VAULT_ADDR: http://127.0.0.1:8200
    command: vault server -config=/vault/config/vault-config.hcl

pg:
    image: percona/percona-distribution-postgresql:17.5-2
    container_name: pg
    ports:
    - "5432:5432"
    environment:
    POSTGRES_PASSWORD: secret
    ENABLE_PG_TDE: "1"
    volumes:
    - ./pgdata:/var/lib/postgresql/data
    - shared-secrets:/etc/postgresql/secrets:ro
    depends_on:
    - vault

volumes:
shared-secrets:

2. Enable the KV v2 secrets engine

In the Vault container, enable a KV v2 storage engine for pg_tde:

vault secrets enable -path=tde -version=2 kv

This creates a tde/ mount for storing encrypted keys.

3. Create a Vault policy for pg_tde

Define a Vault policy that grants pg_tde access to read, write, and list keys.

vault policy write tde-policy - <<EOF
path "tde/data/*" {
  capabilities = ["read", "create", "update", "list"]
}

path "tde/metadata/*" {
  capabilities = ["read", "list"]
}
EOF

This allows pg_tde to:

  • read, create, and update encryption keys
  • list and access metadata for the tde/ path

Tip

If you encounter the following error:

ERROR: failed to get mount info for "http://vault:8200" at mountpoint ...

Ensure your Vault policy includes the following path:

path "sys/mounts/*" {
  capabilities = ["read"]
}

This allows pg_tde to read the mount metadata from Vault.

4. Create an Authentication Method and Token

Enable the AppRole authentication method:

vault auth enable approle
vault write auth/approle/role/tde-role policies="tde-policy"

Generate a token associated with the tde-policy:

vault token create -policy="tde-policy"

Example output:

Key                  Value
token                hvs.{secret_code}
token_policies       ["default" "tde-policy"]

5. Share the token with PostgreSQL

Copy the generated token into the shared secrets directory (shared secrets volume) so PostgreSQL can use it:

echo "hvs.secret_code" > /vault/secrets/vault_token.txt

Tip

You can access this file in PostgreSQL at /etc/postgresql/secrets/vault_token.txt.

6. Register Vault as a global key provider in PostgreSQL

In the PostgreSQL container, connect pg_tde to Vault using:

SELECT pg_tde_add_global_key_provider_vault_v2(
  'vault-provider',
  'http://vault:8200',
  'tde/data/global-key',
  '/etc/postgresql/secrets/vault_token.txt',
  NULL
);

7. Create and set the global master key

Create the global master key:

SELECT pg_tde_create_key_using_global_key_provider(
  'global-master-key',
  'vault-provider'
);

Then set it:

SELECT pg_tde_set_default_key_using_global_key_provider(
  'global-master-key',
  'vault-provider'
);

8. Test encryption with a sample table

Create a sample table:

CREATE TABLE secure_data (
  id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name TEXT,
  amount NUMERIC(10,2),
  created_at DATE
) USING tde_heap;

Then insert data into the table:

INSERT INTO secure_data (name, amount, created_at) VALUES
('Alice', 1234.56, '2025-08-01'),
('Bob', 7890.12, '2025-08-10'),
('Charlie', 345.67, '2025-08-19');

Query the table and confirm that the encryption is functioning:

select * from secure_data;
 id |  name   | amount  | created_at
----+---------+---------+------------
  1 | Alice   | 1234.56 | 2025-08-01
  2 | Bob     | 7890.12 | 2025-08-10
  3 | Charlie |  345.67 | 2025-08-19
(3 rows)

Example global key provider usage

SELECT pg_tde_add_global_key_provider_vault_v2(
    'provider-name',
    'url',
    'mount',
    'secret_token_path',
    'ca_path'
);

Parameter descriptions

  • provider-name is the name to identify this key provider
  • secret_token_path is a path to the file that contains an access token with read and write access to the above mount point
  • url is the URL of the Vault server
  • mount is the mount point where the keyring should store the keys
  • [optional] ca_path is the path of the CA file used for SSL verification

The following example is for testing purposes only. Use secure tokens and proper SSL validation in production environments:

SELECT pg_tde_add_global_key_provider_vault_v2(
    'my-vault',
    'https://vault.vault.svc.cluster.local:8200',
    'secret/data',
    '/path/to/token_file',
    '/path/to/ca_cert.pem'
);

For more information on related functions, see Function Reference.

Required permissions

The PostgreSQL instance requires the following Vault API capabilities to manage and read encryption keys:

  • sys/mounts/<mount>/* - read permissions
  • <mount>/data/* - create, read permissions
  • <mount>/metadata/* - list permissions

Note

For more information on Vault permissions, see the following documentation.

Next steps

Global Principal Key Configuration