Stop Rotating Static Passwords: How Vault Dynamic Secrets Make Credentials Disposable
Vault's dynamic secrets engines generate unique, short-lived credentials on demand and revoke them automatically when the lease expires — eliminating shared static passwords and the rotation toil that comes with them.
08 Oct 2026, 16:07 UTC

Every team that stores a database password in a config file eventually faces the same uncomfortable audit question: who else has seen this credential, and when was it last rotated? The honest answer is usually "everyone who touched the repo" and "never." HashiCorp Vault's dynamic secrets engines attack this problem at the root: instead of distributing a long-lived password, Vault generates a brand-new, short-lived credential on demand and deletes it from the target system when its lease expires.
Static secrets are the actual vulnerability
A static credential fails in two directions at once. It lives too long, so any leak (a committed config file, a log line, a departing employee's laptop) stays exploitable indefinitely. And it's shared, so when something goes wrong you can't tell which application or person used it. Rotation policies help on paper, but manual rotation is the first thing skipped under deadline pressure.
Dynamic secrets invert the model. The application never knows a password in advance. At startup (or when a connection is needed), it authenticates to Vault using its own identity — a Kubernetes service account, an AWS IAM role, an AppRole — and asks for credentials scoped to a role. Vault creates a unique user in the target system, hands back the credentials with a lease, and revokes that user when the lease ends. Each requesting entity gets its own credential, which also gives you per-client audit attribution for free.
How the lease lifecycle works
Every dynamic secret is bound to a lease with a TTL (time to live). The application is expected to renew the lease while it still needs the credential; if the application dies or stops renewing, Vault revokes the credential automatically. This is the property that eliminates secret sprawl: credentials can't outlive the process that requested them, and there's nothing stale left behind to leak later.
Three components make this work:
- Secrets engine: the plugin that knows how to talk to a target system (database, cloud provider, PKI, and others).
- Connection/role configuration: admin-level setup that tells Vault how to reach the target and what SQL or IAM statements to run when creating a user.
- Lease and revocation: Vault's internal lease manager tracks TTLs and calls back into the engine to delete the user on expiry or explicit revocation.
Worked example: dynamic PostgreSQL credentials
The following assumes Vault 1.x with a reachable PostgreSQL instance and an operator holding a root or sufficiently privileged token. Run these from any machine with the Vault CLI configured (VAULT_ADDR and VAULT_TOKEN set).
First, enable the database secrets engine at the default path:
vault secrets enable databaseNext, configure the connection. The root rotation step is important: Vault takes over the admin credential so it isn't shared with humans afterward.
vault write database/config/appdb \
plugin_name=postgresql-database-plugin \
allowed_roles="app-readonly" \
connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/appdb?sslmode=require" \
username="vaultadmin" \
password="initial-admin-password"
vault write -force database/rotate-root/appdbThen define a role. The creation_statements are executed by Vault each time a client requests credentials — here, a read-only user that expires inside PostgreSQL after eight hours as a backstop to Vault's own revocation:
vault write database/roles/app-readonly \
db_name=appdb \
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="8h"An application (or you, testing) then requests credentials. This read requires a Vault token whose policy permits read on database/creds/app-readonly:
vault read database/creds/app-readonlyThe response includes a generated username (typically something like v-token-app-readonly-x1y2z3...), a password, and a lease_id with lease_duration. Verify two things: connect to PostgreSQL with the returned credentials to confirm they work, and query pg_roles on the database to confirm the generated role exists. After the TTL passes (or after running vault lease revoke <lease_id>), query pg_roles again — the user should be gone. That disappearance is the entire point.
Trade-offs and failure modes worth planning for
Dynamic secrets are not free reliability. Three issues come up repeatedly:
- Lease expiry causes downtime if renewal is an afterthought. If your application holds a connection pool open with credentials that expire in an hour, you need a renewal or re-fetch strategy — Vault Agent with template rendering, or the application's own lease-renewal loop. Test the expiry path, not just the happy path.
- The target system takes the load. Every credential request creates a user in PostgreSQL (or an IAM user, or a certificate). A misbehaving client requesting creds in a tight loop can hammer the database's auth catalog. Bound request rates at the client and alert on creation failures.
- Not every system qualifies. The target must support programmatic user creation and deletion with reasonable speed. Legacy systems with manual provisioning or slow user propagation are poor fits; for those, Vault's static roles (where Vault rotates a fixed credential on a schedule) are the pragmatic fallback.
There's also an operational dependency to acknowledge honestly: if Vault is down, no new credentials are issued. Existing leases keep working until expiry, so size your TTLs to survive a Vault outage window you can actually tolerate.
Where to start
Pick one non-production database and one application. Configure the engine and role as above, point a single service at database/creds/..., and force a lease expiry in a test to watch revocation happen. Once you've seen a credential delete itself on schedule, the old model of a password living in an environment variable for two years becomes very hard to defend.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.