Skip to main content
Version: Development

Developer quick start

This quick start explores how to use OpenBao client libraries inside your application code, to store and retrieve your first secret value. OpenBao takes the security burden away from developers by providing a secure, centralized secret store for an application’s sensitive data: credentials, certificates, encryption keys, and more.

warning

Warning: This guide explains how to practice fetching and using secrets, but the explanation relies on an in-memory dev server.

The dev server configuration is useful for practicing with OpenBao locally while you learn, but is insecure and the data isn't durable. You should never use a dev OpenBao instance to protect real secrets, or in any production context.

Prerequisites

  • A development environment applicable to one of the languages in this quick start
    • You can use Go
    • You can use POSIX shell (bash), with curl

This page assumes that you have downloaded OpenBao and have it ready to run on your computer. You can find links to download OpenBao from the Downloads page, along with basic installation instructions. Pick the option that is right for the computer where you want to run OpenBao.

If you download the binary .tar.gz archive, you need to manually extract the bao program from the downloaded archive, and place it somewhere in your path.

If you know how to run OpenBao in a container, you can start that container in server mode (see the next step), and then exec into that container using a command such as docker exec or podman exec. The downloads page lists available container images for OpenBao.

Step 1: start OpenBao

Run the OpenBao server in a non-production "dev" mode:

$ bao server -dev -dev-root-token-id="example-tutorial-token"

The -dev-root-token-id flag for dev servers tells the OpenBao server to allow full ("root") access to anyone who presents a token with the specified value (in this case, example-tutorial-token).

warning

Warning: The root token is useful for development, but allows full access to all data and functionality of OpenBao. In a real cluster you would rotate the root token after initial setup and configure additional controls. Read Authentication, Identity and Policies if you want to set up a secure OpenBao cluster for real use.

OpenBao is now listening over HTTP on port 8200. With the initial setup out of the way, it's time to get coding!

Step 2: authenticate to OpenBao

The bao program is more than the server; it's also a command line tool that you can use to access OpenBao. Imagine for a moment that you are doing IT operations work and you want to interact with an existing OpenBao cluster.

A variety of authentication methods can be used to prove your application's identity to the OpenBao server. To keep things simple for the example, just use the (insecure) root token created in Step 1.

$ export BAO_ADDR=http://127.0.0.1:8200/
$ export BAO_TOKEN_PATH=/tmp/token-for-developer-quickstart
$ bao login

You'll be prompted for the token. Enter the root token (again, this isn't good practice outside of a learning context). The root token is example-tutorial-token. Press enter once you've typed the token in.

You're now able to identify yourself to the local development OpenBao server, as an identity that is authorized to do any action.

If you're using the shell (bash) route through this tutorial, that's it for now. However, if you're writing Go client code, you also need to write the code for programmatic authentication.

Paste the following code to initialize a new OpenBao client that will use token-based authentication for all its requests:

package main

import (
"log"
openbao "github.com/openbao/openbao/api/v2"
)

func main() {
config := openbao.DefaultConfig()

config.Address = "http://127.0.0.1:8200"

client, err := openbao.NewClient(config)
if err != nil {
log.Fatalf("unable to initialize OpenBao client: %v", err)
}

// hard coding a token into source code is an INSECURE practice
//
// this is just an example; in real code you would use other means to let
// the client authenticate to OpenBao
client.SetToken("example-tutorial-token")
}

Check that this compiles and runs OK, without printing any errors.

Step 3: ensure that version 2 key/value storage is available

Run the following command to make sure that the secret/ mount point has been upgraded to version 2:

$ bao kv enable-versioning secret/

OpenBao supports version 1 and version 2 key/value (KV) secrets engines. You'll use KV2, which supports versioning (so that you can go back to the last version of a secret if you need to, for example).

Step 4: store a secret

Secrets are often sensitive data (like API keys or passwords) that don't belong inside source code or even in normal configuration files. In OpenBao, secrets are documents. You can treat a secret like a map between keys and values.

You need to pick a name for the overall document. For this example, name it developer-quickstart. When you see developer-quickstart in the code, that's the document name.

Next, write a secret to OpenBao:

Add this code to the end of the main() function, ensure you have 'context' and 'fmt' (used below) added to imports:

// an example secret.
secretData := map[string]any{
// this example hard-codes the password to store into OpenBao
// in a real application you might generate a secure password
"password": "OpenBao123",
}

mountPoint := "secret" // default path of the KVv2 mount enabled within dev server
nameOfSecret := "developer-quickstart"
_, err = client.KVv2(mountPoint).Put(context.Background(), nameOfSecret, secretData)
if err != nil {
log.Fatalf("unable to write secret %s: %v", nameOfSecret, err)
}

fmt.Println("Secret written successfully.")

…and run that program.

A common way of storing secrets is as key-value pairs using the KV secrets engine (v2). In the code you just added, password is the key in the key-value pair, and OpenBao123 is the value.

The code also provided the path to your secret in OpenBao. You will reference this path in a moment when you practice fetching the secret. The path includes the secret name, developer-quickstart.

Run the code now, and you should see Secret written successfully. If not, check that you've used the correct value for the root token and OpenBao server address.

Step 5: test that the stored secret is correct

$ bao kv get -mount=secret -field=password developer-quickstart

You should see the password ("OpenBao123").

Optional detailed checking

You can fetch the entire secret document, not just the value for the password:

$ bao kv get -mount=secret -format=yaml developer-quickstart

The output is similar to:

data:
data:
password: |
OpenBao123
metadata:
created_time: "2026-04-01T08:52:20.68805668Z"
custom_metadata: null
deletion_time: ""
destroyed: false
version: 1
lease_duration: 0
lease_id: ""
renewable: false
request_id: 23e863b7-e9bb-9f3f-bf4a-3899c38b7372
warnings: null

You can see that the data and metadata for the secret document are there, along with other fields.

Step 6: retrieve the secret using code

Now that you have practiced writing a secret, try to read it. This is what you're most likely to do in a real application.

Remove all the existing code, and replace it with:

package main

import (
"context"
"fmt"
"log"
"os"
openbao "github.com/openbao/openbao/api/v2"
)

func main() {
config := openbao.DefaultConfig()

config.Address = "http://127.0.0.1:8200"

client, err := openbao.NewClient(config)
if err != nil {
log.Fatalf("unable to initialize OpenBao client: %v", err)
}

// hard coding a token into source code is an INSECURE practice
//
// this is just an example; in real code you would use other means to let
// the client authenticate to OpenBao
client.SetToken("example-tutorial-token")

mountPoint := "secret"
nameOfSecret := "developer-quickstart"
secret, err := client.KVv2(mountPoint).Get(context.Background(), nameOfSecret)
if err != nil {
errorMessage := fmt.Errorf("Unable to read secret '%s': %w", nameOfSecret, err)
fmt.Fprintf(os.Stderr, "%s\n", errorMessage)
fmt.Fprintf(os.Stderr, "%s\n", "Check that the secret exists and that you have access to read it")
os.Exit(1)
}

value, ok := secret.Data["password"].(string)
if ok {
fmt.Fprintf(os.Stdout, "Fetched the secret password: %s\n", value)
} else {
log.Fatalf("value type assertion failed: %T %#v", secret.Data["password"], secret.Data["password"])
}
}

…and run the updated program.

If you don't see the secret you expected to see, check that you didn't make a mistake on the way.

That's it! You've just written and retrieved your first OpenBao secret!

Further reading

To learn how to integrate applications with OpenBao without needing to always change your application code, see the OpenBao Agent documentation.