Using HashiCorp Vault Transit to Encrypt Application Data Without Managing Keys
Learn how Vault’s Transit secrets engine provides encryption‑as‑a‑service, enabling key rotation and audit logging while avoiding plaintext key management.
04 May 2026, 12:28 UTC

The problem: encrypting data without managing keys
Many applications need to protect sensitive fields—such as personally identifiable information or API tokens—at rest. The usual approach is to generate, store, and rotate encryption keys yourself, which introduces operational overhead and risk of key leakage. Teams often ask: can we offload encryption to a trusted service while retaining full control over the data?
Why Vault Transit is a practical choice
The Transit secrets engine treats Vault as an encryption‑as‑a‑service endpoint. You send plaintext to Vault, receive back a ciphertext blob, and later send that ciphertext back for decryption. Vault never stores the plaintext; it only manages the encryption key lifecycle. This model gives you:
- Automatic key versioning—rotate keys without re‑encrypting existing data.
- Convergent encryption—derive per‑context keys from a named key and an application‑provided context, useful for deterministic lookups.
- Detailed audit logging—every encrypt/decrypt request is recorded with key name, nonce, and client token.
How Transit works under the hood
When you enable the Transit engine and create a named key (e.g., app-key), Vault generates an internal AES‑256‑GCM key version. An encrypt request includes the plaintext (base64‑encoded) and optional context; Vault returns a ciphertext prefixed with the key version (e.g., vault:v1:...). Decrypt simply reverses the process. Because the ciphertext carries the version identifier, Vault can use the correct key even after rotation, and old ciphertexts remain readable.
Worked example: enabling Transit and encrypting a field
Assume you have a Vault dev server running locally and a token with policies that allow transit:read and transit:update on the app-key path.
- Enable the Transit engine (run on a machine with network access to Vault):
vault secrets enable transit - Create a key named
app-key:vault write -f transit/keys/app-key - Encrypt a plaintext value (replace
my-secretwith your data):
The response includes a fieldvault write transit/encrypt/app-key plaintext=$(echo -n "my-secret" | base64)ciphertextsuch asvault:v1:.... - Store that ciphertext in your application database.
- When you need the original value, decrypt:
The response returnsvault write transit/decrypt/app-key ciphertext="vault:v1:..."plaintext(base64‑encoded); decode it to obtain the original secret.
Where to run: Any host with the Vault CLI configured (VAULT_ADDR and VAULT_TOKEN environment variables). Permissions: The token must be bound to a policy granting transit:update (encrypt) and transit:read (decrypt) on the app-key path. Risk: If the token is leaked, an attacker can encrypt or decrypt data using your key; rotate the token immediately if compromise is suspected.
Trade‑offs and limitations
Transit is not a drop‑in replacement for all encryption needs. Consider these points before adopting it:
- Availability dependency: Encrypt/decrypt calls are synchronous RPCs to Vault. If Vault becomes unavailable, your application cannot encrypt or decrypt data. Deploy Vault in a high‑availability mode (e.g., integrated storage with raft) and monitor the health endpoint.
- Payload size: Transit expects base64‑encoded plaintext; the HTTP request size limit (default 32 MiB) constrains the usable plaintext to roughly a few megabytes. For larger blobs (files, backups), encrypt the data yourself with a data‑encryption key stored in Vault, or use Vault’s transit to wrap that key.
- Latency: Each operation adds network round‑trip time plus Vault processing. In a co‑located setup you can expect a few milliseconds per call; measure with a simple benchmark to ensure it fits your SLA.
Practical way to verify the setup
After enabling Transit and creating a key, you can confirm correct behavior without claiming any specific test results:
- Run an encrypt and then a decrypt as shown above; ensure the decoded plaintext matches the original input.
- Check the audit device (e.g., file audit at
/var/log/vault_audit.log) for two entries: oneupdate(encrypt) and oneread(decrypt) referencingapp-keyand the same nonce. - Execute a loop of 100 encrypt calls and record the elapsed time; compare the average latency to your baseline expectations.
If the audit logs show the expected entries and the round‑trip succeeds, the engine is functioning as designed.
Actionable closing
Vault Transit offers a low‑friction way to delegate encryption key management while retaining auditability and the ability to rotate keys without re‑encrypting existing data. To make it work reliably in production:
- Deploy Vault with HA and enable the transit engine.
- Create minimal policies that grant only the needed encrypt/decrypt capabilities.
- Monitor audit logs for anomalous encrypt/decrypt spikes.
- Benchmark latency under realistic load and size your Vault cluster accordingly.
- For data larger than a few megabytes, consider encrypting with a data‑key that Transit wraps, rather than sending the raw blob through Transit.
By following these steps, you can protect sensitive application fields without the operational burden of managing encryption keys yourself.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.