Using Vault Dynamic Secrets to Generate Short-Lived Database Credentials
Learn how HashiCorp Vault's dynamic secrets engine creates unique, temporary database users on demand, eliminating long-lived passwords and reducing credential leakage risk.
25 Aug 2025, 05:59 UTC

The Problem with Static Database Credentials
Many services rely on a single database user whose password is stored in configuration files or environment variables. If that credential is exposed, an attacker gains unlimited access until the password is manually rotated, which often requires downtime.
How Vault Dynamic Secrets Work
Vault's database secrets engine acts as a privileged administrator. When an application asks for credentials, Vault creates a unique user in the target database with a defined time-to-live (TTL). When the lease expires, Vault automatically revokes the user.
- Role definition: Administrators write the SQL statements Vault will use to create the user and grant permissions.
- On‑demand generation: A read operation on the secrets path triggers the creation statements.
- Lease management: Vault tracks the lease and executes revocation SQL when the TTL ends or when the lease is manually revoked.
Example: Configuring PostgreSQL
Assume you have a Vault server (v1.13+) and a PostgreSQL instance reachable from the Vault host. You need a PostgreSQL role with CREATEROLE privileges to set up the engine.
- Enable the database secrets engine.
- Configure the connection to PostgreSQL.
- Create a role that defines the user creation SQL and TTL.
- Request dynamic credentials.
# 1. Enable the engine (run on a host with Vault CLI, needs Vault admin token)
vault secrets enable database
# 2. Configure PostgreSQL connection
# Replace {{POSTGRES_PASSWORD}} with the superuser password
vault write database/config/pg plugin_name=postgresql-database-plugin allowed_roles="app-readonly" connection_url="postgresql://postgres:{{POSTGRES_PASSWORD}}@localhost:5432/postgres?sslmode=disable"
# 3. Define a role for read‑only access
vault write database/roles/app-readonly db_name=pg creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" default_ttl="1h" max_ttl="24h"
# 4. Generate a dynamic credential
vault read database/creds/app-readonly
The command in step 4 returns a username and password. To verify, connect to PostgreSQL as a superuser and run:
SELECT usename FROM pg_user WHERE usename LIKE 'v-token-app-readonly%';
You should see a row with a randomly generated username that did not exist before the Vault request.
Operational Trade‑offs
- Database load: Each credential request causes a
CREATE ROLEand later aDROP ROLE. High request rates can increase metadata churn. - Vault availability: If Vault is unreachable, new instances cannot obtain credentials. Deploy Vault in a high‑availability cluster for production.
- Lease renewal: Long‑running workloads must renew the lease via the Vault API before the TTL expires, or they will lose access when the user is dropped.
Verification and Rollback
To confirm the workflow, request a credential, verify the user exists in the database, then manually revoke the lease:
# Obtain the lease ID from the read output (shown as lease_id)
vault lease revoke
After revocation, the same SELECT query should return no rows for that username.
Rollback: If the dynamic engine creates unacceptable load, you can disable it:
vault secrets disable database
This stops new credentials from being issued; existing leases continue to be managed until they expire or are revoked.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.