HashiCorp Vault β KV Version 2
KV version 2 adds versioning, soft-delete, metadata, and check-and-set operations on top of the basic key-value storage. Itβs the default secrets engine in dev mode and the recommended choice for most use cases.
Enable KV v2
# KV v2 is the default version when enabling kv
vault secrets enable -path=kv-v2 kv-v2
# Or explicitly
vault secrets enable -path=kv-v2 -version=2 kv
π‘ Note: In dev mode,
secret/is already a KV v2 engine.
Write and Read Secrets
# Write a secret
vault kv put secret/myapp username="admin" password="s3cur3!" env="prod"
Expected output:
======= Secret Path =======
secret/data/myapp
======= Metadata =======
Key Value
--- -----
created_time 2024-01-15T10:30:00.000000Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
# Read the secret
vault kv get secret/myapp
Expected output:
======= Secret Path =======
secret/data/myapp
======= Metadata =======
Key Value
--- -----
created_time 2024-01-15T10:30:00.000000Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
env prod
password s3cur3!
username admin
Versioning
Every write creates a new version:
# Version 1
vault kv put secret/myapp password="version-1"
# Version 2
vault kv put secret/myapp password="version-2"
# Version 3
vault kv put secret/myapp password="version-3"
# Read latest (version 3)
vault kv get secret/myapp
# Read a specific version
vault kv get -version=1 secret/myapp
vault kv get -version=2 secret/myapp
Reading version 1:
======= Metadata =======
Key Value
--- -----
version 1
====== Data ======
Key Value
--- -----
password version-1
KV v2 API Paths
KV v2 introduces additional path prefixes for data and metadata:
Mount Path: secret/
API Paths:
secret/data/myapp β Read/write secret data
secret/metadata/myapp β Read/write metadata
secret/delete/myapp β Soft-delete versions
secret/undelete/myapp β Restore deleted versions
secret/destroy/myapp β Permanently destroy versions
# Write via API (note the /data/ prefix and nested data)
curl --header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data '{"data": {"username": "admin", "password": "s3cur3!"}}' \
"$VAULT_ADDR/v1/secret/data/myapp"
# Read via API
curl -s --header "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/data/myapp" | jq
β οΈ Important: KV v2 API uses
/data/for secret operations. The CLI handles this automatically, but you must include it in API calls.
Soft-Delete and Undelete
KV v2 supports soft-delete β the data is marked as deleted but can be recovered:
# Soft-delete (marks as deleted, data preserved)
vault kv delete secret/myapp
# Try to read β returns no data
vault kv get secret/myapp
# No value found at secret/data/myapp
# Check metadata β deletion_time is set
vault kv metadata get secret/myapp
# Undelete (restore the latest version)
vault kv undelete -versions=3 secret/myapp
# Read again β data is back
vault kv get secret/myapp
Delete Specific Versions
# Delete specific versions
vault kv delete -versions="1,2" secret/myapp
# These versions now show deletion_time in metadata
# but can be restored with undelete
Permanent Destruction
When you need to permanently remove data:
# Destroy specific versions (PERMANENT - cannot undelete)
vault kv destroy -versions="1,2" secret/myapp
# Delete ALL metadata and versions (nuclear option)
vault kv metadata delete secret/myapp
β οΈ Warning:
destroyandmetadata deleteare permanent and cannot be undone.
Check-and-Set (CAS)
CAS prevents accidental overwrites by requiring the current version number:
# Enable CAS requirement on the mount
vault kv metadata put -cas-required=true secret/myapp
# Now writes must include the current version
vault kv put -cas=1 secret/myapp password="new-pass"
# Success β version was 1, now version 2
# This will fail because the version is now 2, not 1
vault kv put -cas=1 secret/myapp password="another-pass"
# Error: check-and-set parameter did not match the current version
API equivalent:
curl --header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data '{"options": {"cas": 2}, "data": {"password": "new-pass"}}' \
"$VAULT_ADDR/v1/secret/data/myapp"
Max Versions
Control how many versions to retain:
# Set max versions for a specific secret
vault kv metadata put -max-versions=5 secret/myapp
# Set default max versions for the entire mount
vault secrets tune -default-lease-ttl=0 -max-lease-ttl=0 \
-options=max_versions=10 secret/
# Older versions beyond the limit are automatically deleted
Custom Metadata
Attach custom metadata to secrets (does not affect the secret data):
# Set custom metadata
vault kv metadata put \
-custom-metadata=owner="platform-team" \
-custom-metadata=environment="production" \
-custom-metadata=expires="2025-12-31" \
secret/myapp
# View metadata
vault kv metadata get secret/myapp
Expected output:
======= Metadata =======
Key Value
--- -----
cas_required false
created_time 2024-01-15T10:30:00.000000Z
current_version 3
custom_metadata map[environment:production expires:2025-12-31 owner:platform-team]
delete_version_after 0s
max_versions 0
oldest_version 0
updated_time 2024-01-15T11:00:00.000000Z
Quick Reference
| Command | Description |
|---|---|
vault secrets enable -path=kv kv-v2 |
Enable KV v2 |
vault kv put secret/path k=v |
Write (creates new version) |
vault kv get secret/path |
Read latest version |
vault kv get -version=N secret/path |
Read specific version |
vault kv delete secret/path |
Soft-delete latest |
vault kv delete -versions="1,2" secret/path |
Soft-delete specific versions |
vault kv undelete -versions=N secret/path |
Restore soft-deleted version |
vault kv destroy -versions=N secret/path |
Permanently destroy |
vault kv metadata get secret/path |
View metadata |
vault kv metadata put -max-versions=5 secret/path |
Set max versions |
vault kv metadata delete secret/path |
Delete all data and metadata |
vault kv put -cas=N secret/path k=v |
Check-and-set write |
Next: Enabling Engines