HashiCorp Vault β€” KV Version 2

4 minute read

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: destroy and metadata delete are 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