Last year I did an infrastructure audit on a side project and found something embarrassing: eleven services, and secrets scattered across all of them. Database passwords in environment files with world-readable permissions, an API key pasted into a systemd unit, another one sitting in a commented-out line of an old compose file like a landmine.

So I did the responsible thing and reached for HashiCorp Vault — the industry standard, the thing everyone puts on their résumé. And then I read the license again. Vault is BUSL now, the Business Source License. Not open source. Usable today, maybe, but the terms can change again tomorrow, and we've watched that movie before with Redis, Terraform, and Elastic.

That's how I ended up at OpenBao. It took one afternoon to migrate, and it's been running quietly ever since. Here's the whole story, including the part where I explain what OpenBao actually is, because almost nobody outside the infra world has heard of it.

What OpenBao is (and why it exists)

When HashiCorp relicensed Vault under the BSL, the community response was the Linux Foundation forking the last open-source codebase and continuing it under the original MPL-2.0 license. The fork is called OpenBao, it's governed by a foundation rather than a single vendor, and the API is deliberately compatible with Vault. The CLI is even called bao instead of vault, which is a small touch I find charming — like the project politely declining to keep the brand's last name.

The pitch in one sentence: everything Vault CE did, OpenBao still does, under a license that can't be taken back.

Spin it up in dev mode

The fastest way to see it work is dev mode — single command, in-memory storage, unsealed for you, for experiments only:

docker run --rm -p 8200:8200 openbao/openbao:latest server -dev -dev-root-token-id=root 

Then in another terminal:

export BAO_ADDR=http://127.0.0.1:8200 export BAO_TOKEN=root bao secrets enable -path=secret kv-v2 bao kv put secret/myapp db-password='s3cret!' api-key='abc123' bao kv get secret/myapp 

There it is — your first secret out of the plaintext file and into something that encrypts at rest, audits every access, and hands out scoped tokens instead of the master key.

The real deployment

Dev mode is a demo. Production looks more like this in compose:

services: openbao: image: openbao/openbao:latest command: server ports: - "127.0.0.1:8200:8200" volumes: - ./bao-config.hcl:/openbao/config/config.hcl:ro - openbao-data:/openbao/data cap_add: - IPC_LOCK restart: unless-stopped volumes: openbao-data: 

With a minimal config:

storage "raft" { path = "/openbao/data" node_id = "node1" } listener "tcp" { address = "0.0.0.0:8200" tls_disable = false tls_cert_file = "/openbao/config/tls.crt" tls_key_file = "/openbao/config/tls.key" } api_addr = "https://bao.mydomain.com:8200" cluster_addr = "https://127.0.0.1:8201" ui = true 

Bind to localhost, put your reverse proxy in front for TLS — same pattern as everything else we self-host. IPC_LOCK matters; without it Bao can't lock memory and will warn you every boot.

Init and unseal — the part that scares people

Run bao operator init once. You get back five unseal keys and a root token, printed once, never again. This is Vault's famous (and infamous) design, unchanged in the fork: the master key is split via Shamir's secret sharing, and a threshold of key shares (default 3 of 5) is required to unseal after every restart.

It's operationally annoying and cryptographically sound. My five shares live in different places — two with me, one in a password manager, one offline, one with a trusted friend who runs infrastructure and understands the ceremony. The root token goes in the password manager and never into an application.

For day-to-day unsealing on a single-node home setup, you can configure an auto-unseal against a cloud KMS, or accept a manual bao operator unseal after reboots like I do. My server reboots maybe four times a year. Typing three key shares four times a year is a fine price for secrets that stay encrypted even if the disk walks away.

Migrating from Vault: easier than expected

Because OpenBao kept the API compatible, migration is mostly renaming:

  • Environment variables: VAULT_ADDR → BAO_ADDR, VAULT_TOKEN → BAO_TOKEN
  • CLI: vault kv ... → bao kv ...
  • SDKs: the Go SDK paths carry over with the endpoint swapped

I stood up OpenBao alongside my Vault instance, restored from a Raft snapshot, walked every service to new short-lived tokens, and shut Vault down a week later when the logs stayed quiet. The whole thing took an afternoon plus that week of paranoia. If you're coming from a fresh start rather than a migration, it's even simpler — there's nothing to translate.

Should you bother?

Depends where your secrets live right now. If they're in .env files and you have fewer than five services, the honest answer is: fix your file permissions first, then come back. OpenBao earns its keep when you have many services, many humans, or compliance reasons to want audit logs and per-service tokens with TTLs.

And if you're choosing between Vault CE and OpenBao for a new deployment — that one's easy. Same software, same skills, same API, but one of them can't have its license pulled out from under you in three years. I've been burned by that pattern enough times to stop volunteering for it.

The boring, invisible, encrypts-your-secrets-and-audits-everything tool is exactly what infrastructure should be. OpenBao is that, minus the licensing asterisk. Set it up once, and your future 2 AM self — the one debugging why the app can't reach its database — will at least know where the password lives.