Skip to content

Hashicorp Vault: An Introduction to the Secrets Management Application

By Sebastian Günther

Posted in Hashicorp_vault

Application hosting is complex and manifold: starting with dedicated programs running on bare metal or virtual severs, to containers on dedicated servers or as a fleet managed by an orchestration software like Kubernetes. Most applications require secrets, access credentials for databases or services, or keys to process encrypted data. From an operations point of view, managing secrets coherently and effectively across on-premise and cloud provider hosted applications is a crucial task.

Hashicorp Vault is a secrets management application. It can be installed in any environment, and then provides configurable API paths for issuing tokens that provide fine-grained access to required secrets.

This article starts a new series about Hashicorp Vault. It provides the essential information’s to start Vault server, configure system access, and store secrets with an internal secrets’ engine. It also covers the essential system components.

The technical context of this article is hashicorp_vault_v.1.20, published 2025-06-25. All provided information and command examples should be valid with newer versions too, baring update to the syntax of CLI commands.

Installing the Hashicorp Vault Binary

Hashicorp Vault is provided as a single binary for several Linux distributions and other platforms. On a Linux computer, you can simply download the appropriate binary file:

curl --output /tmp/vault.zip https://releases.hashicorp.com/vault/1.20.0/vault_1.20.0_linux_amd64.zip
unzip /tmp/vault.zip -d /tmp
chmod +x /tmp/vault
mv /tmp/vault /usr/local/bin

Test that the binary is available by running the following command:

> vault --version

# Log messages
Vault v1.20.0 (6fdd6b59e97d97a9e19b0fb5304bf879c190295e), built 2025-06-23T10:21:30Z

Vault Binary: Functional Overview

The vault binary provides CLI access to most functions of a Hashicorp Vault server or cluster, as well as commands specific to secret management. For the scope of this article, only a small part of the commands are required. Here is the overview:

  • server: Starts a server process
  • operator: Configure a vault cluster (or single instance)
  • secrets: Configure secrets engine
  • kv: Direct access to the key-value secrets engine

Starting a Vault Server

Note: For simplifying operating a vault cluster, a development mode can be used. However, this article intents an introduction based on production-grade processes, and therefore explains commands targeting the complete Vault operation lifecycle.

Every vault server instance needs a configuration file with three essential parts: local and cluster instance information, network configuration for its API, and a storage backend. A basic, runnable configuration file as provided in the official Hashicorp Vault tutorial about Set up a Vault dev server is this:

# Source: https://developer.hashicorp.com/vault/tutorials/get-started/setup
api_addr      = "http://127.0.0.1:8200"
cluster_addr  = "http://127.0.0.1:8201"
cluster_name  = "vault"
disable_mlock = true
ui            = true

listener "tcp" {
  address     = "127.0.0.1:8200"
  tls_disable = true
}

backend "raft" {
  path    = "/etc/hashicorp_vault/raft_storage/"
  node_id = "node01"
}

To start a single instance Vault server, run the following command on the target machine:

> vault server -config=./vault_config.hcl

# Log messages
==> Vault server configuration:

Administrative Namespace: 
             Api Address: http://127.0.0.1:8200
                     Cgo: disabled
         Cluster Address: http://127.0.0.1:8201
   Environment Variables: ...
              Go Version: go1.24.4
              Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", disable_request_limiter: "false", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: 
                   Mlock: supported: false, enabled: false
           Recovery Mode: false
                 Storage: raft (HA available)
                 Version: Vault v1.20.0, built 2025-06-23T10:21:30Z
             Version Sha: 6fdd6b59e97d97a9e19b0fb5304bf879c190295e

==> Vault server started! Log data will stream in below:

2025-06-29T11:20:49.816+0200 [INFO]  proxy environment: http_proxy="" https_proxy="" no_proxy=""
2025-06-29T11:20:49.965+0200 [INFO]  incrementing seal generation: generation=1
2025-06-29T11:20:49.965+0200 [INFO]  core: Initializing version history cache for core
2025-06-29T11:20:49.965+0200 [INFO]  events: Starting event system
2025-06-29T11:21:00.750+0200 [INFO]  core: security barrier not initialized
2025-06-29T11:21:00.750+0200 [INFO]  core: security barrier not initialized
2025-06-29T11:21:00.750+0200 [INFO]  core: seal configuration missing, not initialized

When a Vault server starts, most of its functionality is disabled. In Vault parlance, it is sealed, its secrets can not be accessed. And when a new, single instance is started, it needs to generate the unseal keys.

Unsealing requires to enter a minimum number of all available key shares. To generate 5 keys and require 2 of them for unsealing, run the following command in a new terminal:

export VAULT_ADDR="http://127.0.0.1:8200"
vault operator init -key-shares=5 -key-threshold=2

The concrete unseal keys are printed to the terminal - copy and store them.

Unseal Key 1: mDu2BlVT4XmGEKv7mu2aG4b3aRMTuP27dG67bn6QsKG1
Unseal Key 2: OgMEN5ul8tvcxaN1K38lP12sZgR5/Bb1SzGMFNhwYhMP
Unseal Key 3: gcFcm94ivGBdMl+R3vptnBe/STTGI2LpWlxhSB1VRkur
Unseal Key 4: 85jC/L8aiBLKESltmKf2ZOlg3Dt+hbOp4LitHFQqhtVD
Unseal Key 5: rvwIABjZ/U9NBe+gsFjHMuS5my1bjBzaBQKOrmqhEx9P

Initial Root Token: hvs.uzEHZBQpWuWKlmeUYx4KY6UZ

Then, enter two keys as exemplified in the following:

> vault operator unseal mDu2BlVT4XmGEKv7mu2aG4b3aRMTuP27dG67bn6QsKG1

> vault operator unseal 85jC/L8aiBLKESltmKf2ZOlg3Dt+hbOp4LitHFQqhtVD

# Log messages
Key                     Value
---                     -----
Seal Type               shamir
Initialized             true
Sealed                  false
Total Shares            5
Threshold               2
Version                 1.20.0
Build Date              2025-06-23T10:21:30Z
Storage Type            raft
Cluster Name            vault
Cluster ID              476afccd-f98e-e299-829e-8efbe3c482a7
Removed From Cluster    false
HA Enabled              true
HA Cluster              n/a
HA Mode                 standby
Active Node Address     <none>
Raft Committed Index    32
Raft Applied Index      32

Once completed, the vault UI is shown.

Key-Value Store Secrets Engine

In Hashicorp Vault, a secrets engine is a plugin for writing, reading, and deleting secret data. Each engine has different capabilities and features to store and encrypt data. Several secret engines are supported - the GUI provides a grouped overview:

A built-in secrets engine, available since very early versions of Hashicorp Vault, is key-value, abbreviated as kv. Just as the name implies, this engine allows to store arbitrary key-values pair. The engine is available as a verion1 and version2, where the latter also allow to store versioned secrets.

The following sections provide a compact introduction to initializing and using the kv-v2 secrets engine.

Activation

Before any secret’s engine can be operated, it needs to get activated with the vault secrets top level command. This command itself accepts different sub commands and flags, but to keep this article concise, only the secrets activation will be covered. Execute the following command to active the kv-v2 engine:

vault login
vault secrets enable -max-lease-ttl=1h -path=kv2 kv-v2

This command structure is enable [FLAGS] kv-v2, with following meaning for the flags:

  • max-lease-ttl: The access to a secret is governed via tokens, and each token has a defined TTL. The max lease TTL is the maximum amount of time in which a token can be renewed.
  • path: All Hashicorp Vault functions are exposed via an API that supports a REST-like interface. All secrets are provided with the /secrets suffix, and the path variable is its sub path. Therefore, the above command exposes the kv-v2 engine at the path /secrets/kv2

Data Manipulation

The vault binary defines the handy vault kv subcommand to interact with the engine. It provides essential subcommands to store, read, and delete data.

To get familiar with the command structure, let’s assume a use-case in which a secret called api-key is stored and retrieved. Furthermore, additional versions of this secret are stored, then an earlier version deleted, and finally a complex value consisting of multiple keys is stored.

To store the api-key secret, execute the following command:

vault kv put -mount=kv2 apps api_key=15baab8378896bf

The output of each command shows helpful information about the applied operation.

== Secret Path ==
kv2/data/apps

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-30T16:28:56.799416Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

Lets save another version of this key:

vault kv put -mount=kv2 apps api_key=b1952df28e97f550

The version increments automatically.

== Secret Path ==
kv2/data/apps

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-30T16:29:22.393146Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

Now lets read version 1 of this secret again.

vault kv get -mount=kv2 -version=1 apps

The essential flag is the aptly named version - just add the desired version as a number.

== Secret Path ==
kv2/data/apps

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-30T16:28:56.799416Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

===== Data =====
Key        Value
---        -----
api_key    15baab8378896bf

It is crucial to understand that the apps path can only store a single key-value pair.

When the api_user key is stored ...

vault kv put -mount=kv2 apps api_user=example

… it results in three different version of the apps secret.

The complete history of a secret can be revealed with the following command:

vault kv metadata get -mount=kv2 apps

It shows that three versions were created:

= Metadata Path =
kv2/metadata/apps

========== Metadata ==========
Key                     Value
---                     -----
cas_required            false
created_time            2025-06-30T16:28:56.799416Z
current_version         3
custom_metadata         <nil>
delete_version_after    0s
max_versions            0
oldest_version          0
updated_time            2025-06-30T16:37:49.305843Z

====== Version 1 ======
Key              Value
---              -----
created_time     2025-06-30T16:28:56.799416Z
deletion_time    n/a
destroyed        false

====== Version 2 ======
Key              Value
---              -----
created_time     2025-06-30T16:29:22.393146Z
deletion_time    n/a
destroyed        false

====== Version 3 ======
Key              Value
---              -----
created_time     2025-06-30T16:37:49.305843Z
deletion_time    n/a
destroyed        false

Secrets stay in the version history until the maximum number of versions were stored. With a default value of 10, the very first secret would be erased when the 11 version is stored. Alternatively, a specific version can be deleted with the same named command.

Let’s delete the first version of the apps secret.

vault kv delete -mount=kv2 -versions=1 apps

However, this only marks the version for deletion. The actual deletion requires another command:

> vault kv destroy -mount=kv2 -versions=1 apps

# Log messages
====== Version 1 ======
Key              Value
---              -----
created_time     2025-06-30T16:28:56.799416Z
deletion_time    2025-06-30T16:42:23.336321Z
destroyed        true

Now, if the deleted version is accessed, the command only prints the metadata, not the actual value:

vault kv get -mount=kv2 -version=1 apps    

# Log messages
======= Metadata =======
Key                Value
---                -----
created_time       2025-06-30T16:28:56.799416Z
custom_metadata    <nil>
deletion_time      2025-06-30T16:42:23.336321Z
destroyed          true
version            1

Finally, lets store a version of the apps secret which it itself a JSON-formatted string with the two attributes api_user and api_key.

First, the JSON encoded secret needs to be stored in a file, for example at the location /tmp/data.json.

{
  "api_user":  "example",
  "api_key": "15baab8378896bf"
}

Then this JSON data can be attached to the command using the @ signifier and the full file path.

vault kv put kv2/apps value=@/tmp/data.json    

And to show the resulting config, run the following:

> vault kv get kv2/apps 

# Log messages
==== Secret Path ====
kv2/data/credentials

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-30T16:58:21.323787Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

==== Data ====
Key      Value
---      -----
value    {
  "api_user":  "example",
  "api_key": "15baab8378896bf"
}

Conclusion

Applications hosted as process or containers, on bare-metal servers or orchestrated via Kubernetes, require secrets such as access credentials or keys for processing encrypted data. Hashicorp Vault is a tool that supports effective and secure secrets management. This introductory article showed how to start a single instance Vault server, beginning with the installation of the binary file, and configuration file. It than showed the base processes of generating unseal keys and unsealing the vault. Finally, a key-value secrets engine was added, data stored and delete, and version information accessed.