Choosing Between Vault Dynamic Secrets and Transit Encryption for Secure Credential Management
A decision guide comparing Vault Dynamic Secrets and Transit Encryption, with a comparison table, trade‑off analysis, and concrete CLI examples for PostgreSQL credentials and envelope encryption.
25 Mar 2026, 12:18 UTC

Decision Context
Teams adopting HashiCorp Vault often need to protect two different asset types: short‑lived service credentials (database passwords, cloud API tokens) and arbitrary application data (PII, configuration blobs). Vault offers two native primitives for these jobs—Dynamic Secrets and Transit Encryption. The choice hinges on whether you need Vault to *generate* credentials on demand or to *encrypt/decrypt* payloads you already control.
Constraints to Evaluate
- Supported backend – Dynamic secrets require a secrets engine that can issue leases (e.g., databases, AWS, Azure). Transit works with any data you can send to the HTTP API.
- Operational overhead – Dynamic secrets add lease‑renewal logic and revocation handling. Transit adds per‑request cryptographic latency and key‑policy governance.
- Performance envelope – High‑throughput services may feel the extra round‑trip to Vault for each credential fetch or encrypt call.
- Compliance posture – Some regulations mandate automatic credential rotation (favoring dynamic secrets) while others require envelope encryption with auditable key usage (favoring transit).
Feature Comparison
| Aspect | Dynamic Secrets | Transit Encryption |
|---|---|---|
| Primary purpose | Generate short‑lived credentials for external systems | Encrypt/decrypt arbitrary blobs using Vault‑managed keys |
| Typical backend | Database, Cloud IAM, SSH, PKI | Transit secrets engine (no external dependency) |
| Credential lifecycle | Lease TTL, automatic revocation, renewable | Key versioning, rotation, destruction; data‑key caching optional |
| Integration complexity | Requires configuring a secrets engine and role mapping | Simple API call; only key policy needed |
| Performance impact | One Vault read per lease acquisition; negligible after caching | Encrypt/decrypt per payload; latency ~1‑3 ms per call on modest hardware |
| Risk surface | Mis‑scoped roles can over‑grant; lease leakage if client logs credentials | Over‑permissive key policies expose plaintext; key export if allow_plaintext_backup enabled |
| Version notes | Full database engine stability from Vault 1.12+; earlier versions lack some revocation features | Transit stable since 0.10; convergent encryption added in 1.5 |
Trade‑off Summary
If your workload needs *fresh* credentials for a supported service (PostgreSQL, MySQL, AWS IAM, etc.) and you can tolerate the lease‑management code, dynamic secrets give you automatic rotation and audit trails with minimal application changes. Choose transit when you already possess the secret material and need envelope encryption, key‑hierarchy control, or want to offload cryptographic operations from the application layer.
Concrete Implementation Walk‑through
1. Enable and Test Dynamic Secrets (PostgreSQL Example)
Run the following on a machine with the Vault CLI (vault) and a token that has sudo or root policy (or a custom policy granting database/* capabilities).
# 1. Enable the database secrets engine
vault secrets enable database
# 2. Configure a PostgreSQL connection (replace placeholders)
vault write database/config/my-postgres \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@pg-host:5432/mydb?sslmode=disable" \
allowed_roles=readonly,readwrite \
username="vault-root" \
password="vault-root-password"
# 3. Create a read‑only role with 1‑hour TTL
vault write database/roles/readonly \
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
# 4. Request a lease (this is what your app would call)
vault read database/creds/readonly
Expected check: The response contains lease_id, lease_duration (≈3600s), username, and password. Verify the credential works against the database and that the lease expires after the TTL (use vault lease lookup ).
Risk: If the role’s creation_statements grant more privileges than needed, a compromised lease yields broader access. Keep statements minimal and audit them regularly.
2. Enable and Test Transit Encryption
Same CLI environment, token needs transit/* capabilities.
# 1. Enable transit engine
vault secrets enable transit
# 2. Create a named key with convergence disabled (default)
vault write -f transit/keys/app-data-key
# 3. Encrypt a base64‑encoded payload (replace )
echo -n "sensitive-value" | base64 | \
vault write transit/encrypt/app-data-key plaintext=@-
# 4. Decrypt the resulting ciphertext (replace <ciphertext>)
vault write transit/decrypt/app-data-key ciphertext=<ciphertext>
</code></pre>
<p><strong>Expected check:</strong> The encrypt call returns a JSON field <code>ciphertext</code> prefixed with <code>vault:v1:</code>. The decrypt call returns the original base64 payload; decode it to confirm integrity.</p>
<p><strong>Risk:</strong> A policy that grants <code>transit/decrypt/app-data-key</code> to a broad principal effectively gives that principal read access to all data encrypted with the key. Scope policies to the smallest necessary service identity.</p>
<h2>Verification Checklist</h2>
<ol>
<li>Confirm Vault version ≥ 1.12 for full dynamic‑secret revocation support (<code>vault version</code>).</li><li>Run the dynamic‑secret read command; ensure <code>lease_duration</code> matches <code>default_ttl</code>.</li><li>Execute transit encrypt/decrypt round‑trip; verify ciphertext format and plaintext recovery.</li><li>Inspect audit logs (<code>vault audit list</code>) for lease creation and key usage entries.</li><li>Load‑test a representative workload (e.g., 500 req/s) to measure added latency; adjust caching or batch size if needed.</li>
</ol>
<h2>Limitations & When to Re‑evaluate</h2>
<ul>
<li>Dynamic secrets only work with backends that implement the Vault database plugin interface. Unsupported services (legacy mainframes, custom APIs) cannot use this path.</li><li>Transit does not manage the lifecycle of the *data* you encrypt; you must handle key rotation, re‑encryption, and data‑key caching yourself.</li><li>Both features add a network hop to Vault. In air‑gapped or ultra‑low‑latency environments, consider local credential caches or hardware security modules instead.</li>
</ul>
<h2>Practical Way to Validate the Decision</h2>
<p>Deploy a small‑scale canary: point one micro‑service to dynamic‑secret PostgreSQL credentials and another to transit‑encrypted configuration blobs. Monitor lease renewal success rate, encrypt/decrypt latency, and audit‑log completeness for two weeks. The observed metrics will confirm whether the operational overhead matches your team’s capacity.</p>0 replies
A thoughtful contribution can make all the difference. Be the first to share one.