Vault Secret Storage: Choosing Between KV Static Secrets and Dynamic Database Credentials
Deciding between Vault's KV store and Dynamic Secrets involves balancing operational simplicity against security. This guide compares static and dynamic storage for database credentials.
08 Jul 2025, 10:39 UTC

The Decision: Static vs. Dynamic Secrets
When integrating HashiCorp Vault into an application architecture, the primary decision is whether to store a pre-existing credential (Static) or allow Vault to generate a temporary one on the fly (Dynamic). Choosing the wrong method often leads to either excessive manual rotation toil or unexpected application outages due to credential expiration.
The core trade-off is between simplicity of integration (KV) and reduction of attack surface (Dynamic). Static secrets are essentially a secure vault for a password you already have; Dynamic secrets turn Vault into a credential administrator that manages the lifecycle of the password for you.
Comparison of Storage Methods
| Feature | KV Store (Static) | Database Engine (Dynamic) |
|---|---|---|
| Credential Life | Persistent until manual change | Short-lived (TTL-based) |
| Rotation | Manual or external script | Automatic via lease expiration |
| Blast Radius | High (one key shared by many) | Low (unique key per request) |
| Vault Permissions | Read/Write access to path | Admin access to target Database |
| Dependency | Vault needed for initial fetch | Vault needed for every new lease |
Trade-offs and Constraints
KV Store (Key-Value) is the appropriate choice for API keys, legacy system passwords, or configuration data that does not support programmatic user creation. While KV version 2 provides versioning and soft-deletes, it increases storage overhead and requires a strategy for cleaning up old versions.
Dynamic Secrets are ideal for high-security environments and modern databases. Because Vault creates a unique user for every application instance, you gain an audit trail showing exactly which instance performed which action. However, this introduces a critical dependency: if Vault is unavailable, your application cannot obtain new credentials to connect to the database, potentially causing a system-wide outage.
Implementation: Dynamic Database Secrets
To implement dynamic secrets, Vault must have a privileged account on the target database to execute CREATE USER and DROP USER commands. This example assumes Vault 1.13+ and a PostgreSQL target.
Step 1: Enable the engine and configure connection
Run these commands from a terminal with VAULT_TOKEN exported as a root or admin user:
# Enable the database secrets engine
vault secrets enable database
# Configure the connection to the DB
vault write database/config/my-postgres-db \
plugin_name="postgresql-database-plugin" \
allowed_roles="" \
connection_url="postgresql://{{username}}:{{password}}@postgres.example.com:5432/postgres"
Step 2: Define the Role
Define the SQL statements Vault should use to create the temporary user. This limits the dynamic user's permissions to a specific schema.
vault write database/roles/my-app-role \
db_name="my-postgres-db" \
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"
Step 3: Request and Verify Credentials
Applications request credentials using the role name. This generates a unique username and password pair.
# Request credentials
vault read database/creds/my-app-role
# Verify the lease duration (replace [lease_id] with the ID from the previous command)
vault lease lookup [lease_id]
Verification and Limitations
To verify the result, attempt to log into the database using the generated credentials. You should find that the user exists but possesses only the permissions defined in the creation_statements. Once the default_ttl expires, the database will reject the connection, and Vault will automatically drop the user from the database.
Limitations:
- Connection Pooling: Applications using long-lived connection pools must be configured to refresh credentials before the TTL expires, or they will face
Authentication Failederrors. - Database Overhead: Creating and dropping users frequently can cause bloat in some database system catalogs. Monitor your DB metadata performance if rotating secrets every few minutes.
Rollback
If the dynamic engine causes instability, you can revoke all active leases and disable the engine to return to a static credential model:
# Revoke all leases associated with the engine
vault lease revoke -prefix database/
# Disable the engine
vault secrets disable database0 replies
A thoughtful contribution can make all the difference. Be the first to share one.