Managing Database Credential Lifecycles with Vault Dynamic Secrets
Move beyond static passwords by using Vault's database secrets engine to generate unique, short-lived credentials that expire automatically.
21 Sept 2026, 19:45 UTC

The problem: The risk of long-lived database credentials
Hard-coded database credentials or long-lived passwords stored in configuration files are a primary target for attackers. Once a password is leaked, it remains valid until a human manually rotates it—a process that often causes downtime or is deferred indefinitely due to risk. The goal is to move toward ephemeral credentials: unique, short-lived users that expire automatically.
How Vault dynamic secrets work
Unlike static secrets, which store a pre-existing password, the Vault Database Secrets Engine generates credentials on-the-fly. When an application requests access, Vault connects to the database using a high-privilege root account and executes a set of SQL statements to create a temporary user with a specific set of permissions.
Every set of credentials is tied to a lease. A lease defines the TTL (Time To Live)—the duration the credential is valid. When the lease expires, Vault automatically executes a revocation statement (such as DROP USER or REVOKE) to remove the user from the database, ensuring that leaked credentials have a strictly limited window of utility.
Worked example: Dynamic PostgreSQL roles
This example assumes a Vault 1.15+ cluster and a PostgreSQL 15 instance. You will need a Vault token with administrative permissions for the database secrets engine.
1. Enable the engine and configure the connection
Run these commands from a terminal with the Vault CLI configured. First, enable the engine:
vault secrets enable database
Next, configure Vault to connect to your PostgreSQL instance. Replace placeholders with your actual database details:
vault write database/config/my-postgres \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db.example.com:5432/postgres?sslmode=verify-full" \
username="vault_root" \
password="root-password-here"
Risk: The vault_root user is a high-value target. Ensure this user has the minimum permissions required to create and drop roles, and protect its credentials using strict Vault policies.
2. Define the role and creation statements
Define a role that specifies exactly what the dynamic user is allowed to do. In this case, we grant read-only access to a specific schema:
vault write database/roles/readonly-app \
db_name=my-postgres \
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"
3. Request and verify credentials
The application requests credentials by reading the role path:
vault read database/creds/readonly-app
Vault returns a unique username, password, and a lease_id. To verify the user exists in PostgreSQL, run this query via psql:
SELECT usename FROM pg_user WHERE usename LIKE 'v-token-%%';
Once the TTL expires or you manually run vault lease revoke [lease_id], the user will be automatically removed from the pg_user table.
Trade-offs and operational limitations
- Database Overhead: Every credential request triggers a round-trip to the database to execute DDL statements. High-frequency requests can strain the database's system catalogs. Use a connection pooler (like PgBouncer) at the database level, but remember that Vault does not pool the connections it uses to manage users.
- Application Logic: Applications cannot simply load a password at startup. They must be designed to handle 403 Forbidden or authentication errors by requesting a new lease from Vault and refreshing their connection pool.
- Vault Availability: If the Vault cluster is sealed or loses quorum, leases cannot be renewed. If an application's lease expires during a Vault outage, the application will lose database access until Vault is restored.
Actionable implementation path
- Audit Current Access: Identify which applications can handle dynamic usernames versus those that require a constant username (which would require Static Roles instead).
- Implement Proactive Renewal: Configure your application or Vault Agent to renew the lease at 2/3 of its TTL to avoid unexpected expiration.
- Set Conservative TTLs: Start with a longer TTL (e.g., 24h) to verify stability, then tighten the window to 1h or less as your application's rotation logic is proven.
- Monitor Audit Logs: Track
database/creds/*paths in Vault audit logs to correlate specific application instances with the temporary database users they created.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.