Skip to main content
This lesson covers HashiCorp Vault’s Key/Value (KV) Version 2 secrets engine. You’ll learn how to:
  • Inspect existing secrets engines.
  • Enable KV v2 versioning on an existing mount.
  • Create a new KV v2 mount.
  • Read and write versioned secrets and specific versions.
  • Use soft delete (undelete) vs permanent destroy semantics.
  • Manage metadata and custom metadata.
  • Use the correct path prefixes (data/, metadata/) for policies and the HTTP API.
Links and references:

Inspect existing secrets engines

List enabled secrets engines on the Vault node to identify mounts and their types:
In this environment training/ is a KV v1 mount. The next section shows how to enable versioning to convert it to KV v2.

Enable versioning on an existing KV v1 mount

To convert an existing KV v1 mount (for example training/) into KV v2 (enable versioning):
Verify the mount now reports version:2:
Now training/ is a versioned KV store (KV v2).

Create a new KV v2 mount

You can also enable a fresh KV v2 mount. Two common ways:
  • Explicit engine type:
  • Using the -version=2 option (CLI version dependent):
Example output:
You can add a description at enable time with -description="...".

Write secrets (KV v2) — vault kv put

When using the Vault CLI helper (vault kv), you can omit internal KV v2 prefixes (data/ and metadata/). The CLI will show internal paths in output but does not require you to type them. Write a secret at kvv2/apps/circleci:
Update the secret (this creates version 2):

Read secrets (latest and specific versions)

By default, vault kv get returns the latest version:
To read a specific version, supply -version=<n>:

Delete (soft delete) and undelete

A normal vault kv delete performs a soft delete: it marks the latest version as deleted but retains metadata and previous versions. You can undelete specific versions later. Soft-delete the latest version:
After a soft delete, vault kv get may show metadata but no data for the deleted version. To recover, use vault kv undelete and specify one or more versions to restore:
Undelete accepts multiple versions via the -versions flag (for example: -versions=2,3), which lets you restore several deleted versions at once.

Destroy (irreversible) vs undelete

vault kv destroy permanently removes specified versions from storage — this is irreversible without restoring from a Vault snapshot/backup.
Destroying KV v2 versions is permanent. You cannot recover destroyed versions with vault kv undelete. Only a Vault snapshot/backup can restore destroyed data.
Example: destroy version 1 and observe that it is removed:
Attempting to undelete a destroyed version fails:

View and tune metadata

Inspect metadata and the version history for a key:
Tunable metadata settings:
  • max_versions — how many historical versions to retain (older versions beyond this are garbage-collected).
  • delete_version_after — time-to-live for versions (versions older than this may be removed).

Custom metadata

Attach key/value annotations to a secret using vault kv metadata put with -custom-metadata:
A JSON vault kv get -format=json response will include custom_metadata. Use jq to extract fields:
Custom metadata is useful for annotations like owner, environment, or last-used timestamps.

List keys and fully delete a secret (metadata + data)

List keys under a mount:
To permanently remove a secret path (all versions and the metadata), delete its metadata:
Deleting metadata removes both metadata and all versioned data for that path.

KV v2 in the Vault UI

You can manage KV v2 mounts and keys in the Vault Web UI. Below are screenshots demonstrating the secrets engines list, creating a KV v2 secret, and viewing a saved secret (with masked values):
A screenshot of the HashiCorp Vault web UI on the "Secrets Engines" page showing a list of enabled secret engines (cubbyhole, kv v2, training, transit). The page includes a top navigation bar and a footer with the Vault version.
A screenshot of the HashiCorp Vault web UI showing the "Create secret" form (kv v2) with the path set to "apps/artifactory" and fields for key/value secret data. The page also shows JSON toggle, "Show secret metadata," an Add button for entries, and Save/Cancel controls.
A screenshot of the HashiCorp Vault web UI showing the secret at path "apps/artifactory" with a single key "artifact" whose value is masked. The page is served locally (127.0.0.1:8200) and shows version metadata from Mar 25, 2022.

Policies and KV v2 path prefixes

Policies and the HTTP API map to the internal KV v2 paths; therefore policy path strings must include data/ and metadata/ prefixes. Common mappings: Example policy for kvv2/apps/artifactory:
Note: CLI helper commands (e.g., vault kv put/get/list) hide these prefixes for convenience, but policies and direct API calls must reference the internal prefixes.
CLI helper commands — vault kv put/get/list — abstract away the internal data/ and metadata/ prefixes. When writing policies or calling the HTTP API directly, always include data/ or metadata/ as appropriate.

Calling the KV v2 HTTP API (curl)

When interacting with KV v2 via the HTTP API, include the data/ prefix for reading secret data:
Pretty-print the response with jq:
If you omit data/ in the URL for KV v2 reads, you will not receive the expected data payload structure and policy matches may fail. Use metadata/ for metadata API endpoints (e.g., .../kvv2/metadata/<path>).

Quick reference — commands and prefixes

Summary

  • Convert KV v1 mounts to KV v2 with vault kv enable-versioning.
  • Enable new KV v2 mounts via vault secrets enable -path=<mount> kv-v2.
  • Use vault kv put/get/delete CLI helpers — they hide data/ and metadata/ prefixes.
  • Use vault kv undelete to recover soft-deleted versions; use vault kv destroy to permanently remove versions.
  • Manage retention with metadata (max_versions, delete_version_after) and annotate secrets with custom metadata.
  • For policies and direct API calls, always include the data/ and metadata/ prefixes in paths.
If you want details on CAS (compare-and-swap), conflict handling, or examples for tuning max_versions and delete_version_after, ask and I’ll provide targeted examples.

Watch Video