OpenBao Secrets Management: A Production Tutorial (2026)
Last Updated: October 2026
Most credential leaks are not clever. A database password sits in a Helm values file, gets copied into a CI variable, and lives for three years because rotating it would break something nobody remembers. OpenBao secrets management attacks that pattern at its root: workloads prove who they are, receive a short-lived credential that exists only for them, and lose it automatically when the lease ends. It is the open-source, Linux Foundation-hosted fork of HashiCorp Vault, and it is no longer a mere clone. By October 2026 it has shipped namespaces, horizontal read scaling, declarative self-initialization, pluggable auto-unseal, post-quantum signatures and control groups, several of them ahead of or different from the upstream product.
This tutorial is written for platform and security engineers who need to run it for real. You will build a mental model of the seal and barrier, deploy a Raft cluster, choose an auto-unseal strategy, set up auth and policies, issue dynamic database credentials and certificates, integrate Kubernetes, and plan upgrades and disaster recovery. Everything is tied to the current release line, with the places where I could not verify a detail labelled as such.
What this covers: why the fork exists, the architecture and key hierarchy, a production Raft deployment, seal options, auth and policy design, dynamic secrets, PKI and transit, namespaces, Kubernetes patterns, the 2.4 to 2.7 changes that matter, Vault migration, failure modes and a checklist.
Context and Background
In August 2023 HashiCorp moved Vault, Terraform and most of its portfolio from the Mozilla Public License 2.0 to the Business Source License. BSL code is source-available, not open source, and it restricts production use that competes with the vendor. The reaction was immediate: Terraform became OpenTofu, and a group of Vault users and vendors forked the last MPL-2.0 Vault code as OpenBao under the Linux Foundation. IBM completed its acquisition of HashiCorp in 2025, which did not change the license history but did raise the stakes for teams that wanted a neutral steward for something as central as a secrets broker.
Why does licensing matter more for a secrets manager than for most tools? Because it is the dependency everything else reaches through: databases, CI runners, service meshes, certificate issuance and encryption keys. Procurement, legal and some regulated sectors treat source-available licenses as a long-tail compliance item. A project under neutral governance with a public technical steering committee, MPL-2.0 licensing and no single-vendor veto removes that question, and it lets you patch, embed and redistribute internally without negotiating an exception. The project’s home is openbao.org and the source is at github.com/openbao/openbao.
The second reason to care is that OpenBao is now a moving target in its own right. The first releases were deliberately conservative: rename the binary to bao, keep API paths compatible, fix what was broken. Since then the project has added features that Vault Community Edition either lacks or reserves for paid tiers, and it has begun removing things it does not want to maintain. As of the official GitHub releases page, the current line is 2.7, with v2.7.1 published on 1 October 2026 and v2.6.4 backporting security fixes the same day. If your mental model of OpenBao is “Vault 1.14 with a different name”, it is two years out of date.
The competitive field is also wider than it was. Cloud-native alternatives such as AWS Secrets Manager, Azure Key Vault and Google Secret Manager are fine when you live inside one cloud, and the External Secrets Operator can sync from any of them into Kubernetes. What none of them gives you is a single, portable, policy-driven broker that also issues database credentials, SSH certificates, X.509 certificates and encryption-as-a-service, on any infrastructure, including air-gapped plants and edge sites. That portability is why OpenBao shows up in industrial and IoT estates as often as in cloud-native ones.
OpenBao Architecture: The Barrier, the Seal and the Request Path
A running OpenBao server is a single Go binary that brokers every secret request. Clients never touch storage. They authenticate, receive a token, and every request is checked against that token’s policies before a secrets engine responds. All state persisted to disk, whether secrets, leases, policies or configuration, passes through an encryption barrier first.

Figure 1: OpenBao request path. Identity is established by an auth method, authorization is a policy check, and only then does a secrets engine touch the encrypted barrier and storage.
Figure 1 shows the layers. The auth layer converts an external proof of identity (a Kubernetes service account token, an OIDC login, a cloud IAM signature, an AppRole pair) into an OpenBao token. The policy engine evaluates the token’s policies against the requested path and operation. The router then hands the request to the secrets engine mounted at that path, and the engine reads and writes through the barrier into the storage backend. Because each stage is separate, you can audit, rate-limit and reason about them independently.
The key hierarchy and why a stolen disk is useless
The seal documentation describes a chain of keys. Data is encrypted with an encryption key held in a keyring. The keyring is encrypted by a root key, and the root key is encrypted by the unseal key. A freshly started server knows where its storage lives but cannot decrypt anything until it recovers the unseal key, which is the definition of sealed. A stolen Raft snapshot, a copied volume or a leaked backup is ciphertext without that chain, which is why the seal is the single most important control in the system.
The default seal is Shamir: the unseal key is split into shares, and a threshold of shares entered in any order, possibly from different machines and different people, reconstructs it. With auto-unseal, a trusted external service holds the protection instead, and at startup OpenBao asks it to decrypt the root key. Initializing with auto-unseal produces recovery keys instead of unseal keys. Recovery keys cannot unseal the server; they authorize sensitive operations such as generating a root token. We cover the trade-offs in the seal section below.
Storage: why Raft integrated storage is the default choice
OpenBao supports several storage backends, but integrated storage based on the Raft consensus protocol is the one to start with. It has no third-party dependency, provides high availability and supports backup and restore workflows. Under the hood the state machine is stored in BoltDB, writes block until they are committed and applied by a quorum, and applied log entries are truncated automatically so the log does not grow without bound.
The operational numbers follow from Raft’s majority rule. A three-node cluster tolerates one failure and a five-node cluster tolerates two. The documentation recommends five servers per production cluster and strongly discourages a single node, because data loss is likely in a failure. Performance with stable leadership is bound by disk I/O and network latency, since each commit needs a round trip to half the cluster. That has a practical consequence: put Raft on low-latency local SSD or NVMe, not on a network filesystem, and keep voters in one region or in zones with single-digit-millisecond round trips.
Two other details from the integrated storage page are worth remembering. BoltDB does not shrink its file after deletions; freed space is reused but the file stays large, so capacity planning should be based on peak, not average. And all nodes in a Raft cluster must share the same seal configuration, which matters when you plan a seal migration.
Secrets engines: the four you will actually use
Secrets engines are mounted at paths and each does one job. The KV version 2 engine stores static secrets with versioning and check-and-set semantics. The database engine generates credentials on demand, with a lease and an automatic revocation statement. The PKI engine issues short-lived X.509 certificates, acting as an internal certificate authority. The transit engine performs cryptographic operations on data you send it, returning ciphertext without storing the plaintext, which gives you application-layer encryption without scattering key material across services.
Between those four, one deployment replaces what often starts as four separate tools: a config store, a credential rotator, an internal CA and a key service. The cost of that consolidation is that the cluster becomes critical infrastructure, which is why the rest of this tutorial is about availability, recovery and blast-radius control rather than features.
Building a Production Cluster: Raft, TLS and Unseal Strategy
The old advice to start with a single dev-mode node and “harden later” is how teams end up with a production secrets store whose unseal keys live in a Slack message. Start from the target topology: five voters, TLS everywhere, auto-unseal, audit enabled before any secret is written, and the root token revoked after bootstrap.
A five-node configuration
The following is a representative node configuration. Each node gets its own node_id, api_addr and cluster_addr, and retry_join stanzas let a node find its peers on first boot. Treat addresses and paths as placeholders for your environment.
# /etc/openbao/config.hcl (node-1 of 5)
ui = true
cluster_name = "bao-prod-eu"
storage "raft" {
path = "/var/lib/openbao/raft"
node_id = "bao-1"
retry_join { leader_api_addr = "https://bao-2.internal:8200" }
retry_join { leader_api_addr = "https://bao-3.internal:8200" }
retry_join { leader_api_addr = "https://bao-4.internal:8200" }
retry_join { leader_api_addr = "https://bao-5.internal:8200" }
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_cert_file = "/etc/openbao/tls/server.crt"
tls_key_file = "/etc/openbao/tls/server.key"
tls_min_version = "tls13"
}
api_addr = "https://bao-1.internal:8200"
cluster_addr = "https://bao-1.internal:8201"
# Auto-unseal is configured in a seal stanza; see the next section.
Run the service under systemd with LimitMEMLOCK=infinity or an equivalent so the process can lock memory, disable swap on the host, and run as a dedicated unprivileged user. Core dumps should be disabled, because a core file of a running, unsealed server contains key material in the clear. Then initialize exactly once, on one node:
export BAO_ADDR=https://bao-1.internal:8200
export BAO_CACERT=/etc/openbao/tls/ca.crt
bao operator init # with auto-unseal this prints recovery keys
bao status # Sealed false, HA Enabled true
bao operator raft list-peers
With Shamir sealing you pass -key-shares and -key-threshold and distribute the shares to separate custodians; with auto-unseal you receive recovery keys and the node unseals itself. Initialization happens once per cluster, and the printed keys are never shown again. If you lose them before auto-unseal is working, you lose the cluster. If the other four nodes have retry_join configured and share the same seal, they join and unseal automatically, which you can verify with bao operator raft list-peers.
Choosing a seal

Figure 2: Choosing an unseal mechanism. The decision is driven by who you trust to hold the root of trust and what happens when that dependency is unavailable.
Figure 2 frames the decision. The question is not “which is most secure” but “where does the root of trust live, and what happens when it is unreachable at 3 a.m.”.
Shamir shares need no external dependency, which makes them the right choice for small, isolated or air-gapped installs. The cost is human availability: every node restart needs a threshold of custodians. A rolling Raft upgrade of five nodes means fifteen share entries at a three-of-five threshold. For a production cluster that restarts regularly, that is operationally untenable, so Shamir is usually the bootstrap or last-resort mechanism.
Cloud KMS auto-unseal (AWS KMS, Azure Key Vault, Google Cloud KMS, OCI KMS) delegates root-key protection to a managed service with its own IAM, audit and HSM-backed keys. It is the lowest-friction production option on a hyperscaler. The dependency is real: if the KMS key is disabled, scheduled for deletion or the region is unreachable, a restarted node cannot unseal. Protect the key with deletion safeguards and alerts on any key-state change.
Transit seal uses another OpenBao or Vault cluster’s transit engine to wrap the root key. This is the classic pattern for a fleet: one small, carefully guarded “seal cluster” unseals many application clusters, and you spend your HSM or Shamir ceremony budget once. The seal cluster itself must be unsealed by a different mechanism and must be at least as available as everything it unseals.
Static key auto-unseal, introduced in 2.4, is for environments with explicit trust chaining where an external KMS is unavailable: the key is delivered by something you already trust, such as a Kubernetes secret or another secret store. The release notes position it as enabling auto-unseal in constrained environments; the security of the setup is exactly the security of how that key is delivered, so do not mistake it for a free lunch.
KMIP auto-unseal arrived in 2.3 for organizations that already run a KMIP-speaking key manager.
The 2.7 change: seals as plugins
There is an important upgrade trap here. OpenBao 2.6 introduced a kms plugin type so that auto-unseal mechanisms can ship as external binaries, registered with a plugin "kms" "name" { } stanza, and KMS plugins restart automatically after crashes. The 2.6 release notes then announced that the built-in alicloudkms, awskms, azurekeyvault, gcpckms, ocikms and pkcs11 seals would be removed from the binary, and the 2.7.0 notes confirm that these seals are no longer built in and must be installed as external plugins. The HSM-flavoured distribution was discontinued, with PKCS#11 support continuing through a KMS plugin.
The practical effect: if you run on 2.5 or 2.6 with seal "awskms" and simply swap the binary to 2.7, the node will not unseal. Plan the plugin installation, declarative registration and a test restart of a non-leader node as part of the upgrade, not as an afterthought. The 2.7 notes also change plugin_auto_register to default to true and drop the need for a sha256sum for manually installed plugins in declarative registration. Verify the exact stanza syntax for your seal against the current plugin documentation before rollout, because this is the area changing fastest.
Declarative self-initialization
Since 2.4, operators can describe the initial state of the server, including auth methods, audit devices and secret mounts, in configuration and have it applied on first start. This closes the awkward window between “initialized” and “configured” where a root token exists and nothing is audited. The project’s own advice is to keep self-initialization minimal and hand everything else to infrastructure-as-code such as OpenTofu. A sensible split is: self-init enables audit, enables one human OIDC login path and one machine path, creates an admin policy, and nothing more. Everything after that is declarative and reviewed in Git.
Configuration-based audit devices, also added in 2.4, complement this. Audit devices can be created and removed through server configuration, applied on restart or SIGHUP, which means a node cannot come up in a state where audit is silently missing. Enable at least two audit devices if you can, because OpenBao will refuse to service requests if it cannot write to any enabled audit device. That is the right failure mode for a security system, and also a reason to monitor disk space on the audit sink.
Horizontal read scalability
OpenBao 2.5, released 4 February 2026, added horizontal read scalability: standby nodes can serve storage-read requests locally and forward writes to the active leader. Results are eventually consistent, and you can disable the behaviour with disable_standby_reads=true. The 2.7 release adds strong-consistency controls via the X-Vault-Index and X-Vault-Inconsistent headers so that clients that just wrote a value can insist on reading it back consistently, and extends standby reads to PostgreSQL storage when ha_enabled = true with physical replication.
For read-heavy estates (thousands of pods fetching certificates or KV values on a refresh interval) this is a significant capacity lever. It does not make writes scale: every lease creation, token issuance and dynamic credential mint still funnels through the leader. If your workload is dynamic credential churn rather than static reads, size the leader’s disk and CPU, not the number of standbys.
Auth Methods, Policies and Identity Design
A root token can do anything, which is why you stop using it after bootstrap. Real access runs through auth methods that exchange an external identity for a scoped token.

Figure 3: A pod logs in with its service account token, receives a policy-scoped token, and then obtains a leased dynamic database credential. Every response is the product of an identity proof, a policy decision and a lease.
Matching the method to the identity
Different identities need different proofs. Humans should log in through OIDC against your identity provider, which gives single sign-on and an audit trail tied to real people. Kubernetes pods authenticate with their service account token via the Kubernetes auth method, which validates the token against the cluster API. CI jobs can present a JWT issued by the CI platform. Virtual machines can use cloud IAM methods or AppRole when nothing better exists. Certificate auth fits devices that already hold an X.509 identity, which is common in industrial fleets.
The principle behind all of these: the best secret is the one a workload never has to be given. If the platform can already vouch for a workload (a service account, a cloud instance identity, a SPIFFE identity), use that as the login credential and avoid bootstrapping yet another static secret. If you run SPIFFE, our piece on SPIFFE and SPIRE workload identity covers how to feed those identities into an auth method.
AppRole remains useful when no platform identity exists, but it is the one to design carefully. A RoleID is not sensitive; it merely names the role. The SecretID is the credential and must be delivered just in time, ideally with response wrapping so the orchestrator hands the workload a single-use wrapping token, and the workload unwraps it once. If an attacker unwraps first, the legitimate unwrap fails and you have a detection signal.
Recent releases also tightened this area. The 2.6.4 security backports fixed a case where AppRole SecretIDs could be used for login after expiry but before the tidy operation ran (GHSA-7m59-mp95-w6ph), and made Kubernetes auth validate service account JWTs on renewal so a revoked token cannot keep renewing (GHSA-gf3j-hm38-jhh5). Both are reminders to keep patch releases flowing, not to pin to the version you first deployed.
Policies: default deny, path-based
Policies are HCL documents granting capabilities (create, read, update, delete, list, sudo, deny) on paths, and anything not granted is denied. Note the KV v2 gotcha that bites everyone once: the API path for reading a secret is secret/data/<path>, listing uses secret/metadata/<path>, and a policy written against secret/<path> silently grants nothing.
# payments-app.hcl
path "secret/data/payments/*" {
capabilities = ["read"]
}
path "database/creds/payments-ro" {
capabilities = ["read"]
}
path "transit/encrypt/payments" {
capabilities = ["update"]
}
path "transit/decrypt/payments" {
capabilities = ["update"]
}
# Allow the workload to look at and renew its own token
path "auth/token/lookup-self" { capabilities = ["read"] }
path "auth/token/renew-self" { capabilities = ["update"] }
Identity templating lets one policy serve many workloads, for example scoping access to secret/data/{{identity.entity.aliases.<mount_accessor>.metadata.service_account_namespace}}/*. Be aware of a 2.6 upgrade note: identity template values containing +, * or / are rejected by default, with override flags (allow_wildcards_in_identity_templates, allow_slashes_in_identity_templates) available. Resist the override. A template that can expand to a wildcard is a privilege-escalation path waiting for someone to name a namespace cleverly.
The 2.7 release adds control groups: a control_group ACL stanza that requires a second party to approve requests on specified paths. This is the two-person rule for operations like reading a root CA key or deleting a mount, and it is worth using for the handful of paths where a single compromised operator account would be catastrophic.
Tokens, leases and the blast-radius budget
Every dynamic secret is attached to a lease, and leases are attached to the token that created them. Revoke the token and its child leases are revoked. That tree is the mechanism that makes “revoke everything this pod ever received” a single command, and it is the reason you want short token TTLs with renewal rather than long-lived tokens. As a rule of thumb, set token_ttl to something the workload can renew comfortably (an hour) and token_max_ttl to a ceiling after which the workload must re-authenticate (a working day at most), then monitor lease counts, because unbounded lease growth is the most common slow-motion failure of a busy cluster.
Dynamic Secrets, PKI, Transit and Namespaces in Practice
Dynamic database credentials
The database secrets engine is the feature that changes behaviour. Instead of handing an application the password for a shared account, OpenBao connects to the database with a privileged service account and creates a unique user per request, with a lease. When the lease expires or is revoked, OpenBao runs the revocation statement and the user disappears.
bao secrets enable database
bao write database/config/orders-pg \
plugin_name=postgresql-database-plugin \
allowed_roles="payments-ro" \
connection_url="postgresql://{{username}}:{{password}}@pg.internal:5432/orders?sslmode=verify-full" \
username="bao-admin" password="CHANGE-ME"
# Rotate the admin password so only OpenBao knows it
bao write -f database/rotate-root/orders-pg
bao write database/roles/payments-ro \
db_name=orders-pg \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl=1h max_ttl=24h
bao read database/creds/payments-ro
Three details separate a demo from production. First, run rotate-root so the bootstrap administrative password you typed is no longer valid anywhere; otherwise the secrets store holds a credential that also lives in your shell history. Second, keep creation statements minimal and per-role: a read-only role should grant read-only privileges, not inherit from a powerful group. Third, remember that revocation is a database operation that can fail. The 2.5 release changed database plugins to ignore “not found” errors on revoke by default, which avoids leases stuck in a failed-revoke loop when a user was already dropped, but you should still alert on revocation errors.
The number to watch is connection pressure. If each pod mints credentials at startup and holds a connection pool, a rolling deployment of a hundred pods creates a hundred database users and a burst of logins. Use a sensible default TTL, let applications renew rather than re-request, and consider pooling proxies where connection limits are tight. Static roles, which rotate the password of a pre-existing account on a schedule, exist for databases or legacy apps that cannot cope with ephemeral users.
PKI as an internal certificate authority
The PKI engine turns OpenBao into a CA that issues certificates on demand. The mature pattern is an offline root CA that signs an intermediate, with the intermediate living in OpenBao and issuing leaf certificates of hours or days rather than years. Short lifetimes make revocation largely a non-problem, because a stolen certificate expires before it is useful.
bao secrets enable -path=pki_int pki
bao secrets tune -max-lease-ttl=43800h pki_int
bao write pki_int/roles/mtls-workloads \
allowed_domains="svc.internal" allow_subdomains=true \
max_ttl=72h require_cn=false
bao write pki_int/issue/mtls-workloads common_name="orders.svc.internal" ttl=24h
Recent releases added knobs relevant to this: not_after_bound and not_before_bound role parameters (2.3) to cap issuance windows, allowed_ip_sans_cidr (2.5) to constrain IP SANs, and a 2.7 change that rejects fully qualified names with a trailing dot. The 2.7 release also adds ML-DSA (FIPS 204) post-quantum signatures for CA, CSR and leaf operations in PKI, and for mldsa-44, mldsa-65 and mldsa-87 keys in transit, plus listener options to enforce post-quantum key exchange preferences. Treat that as an early-adopter capability: it is excellent for building a crypto-agility roadmap and testing client compatibility, but verify what your TLS libraries and HSMs support before issuing PQC certificates to production traffic.
Also new in 2.7 are External Keys, where PKI and transit can use KMS plugins so key material stays outside OpenBao, configured through the /sys/external-keys APIs. For a CA this is the closest the open-source line gets to “the CA key never leaves the HSM”, and it is worth evaluating if you have a hardware key requirement.
Transit: encryption without key sprawl
Transit never stores your data. An application posts plaintext (base64-encoded), receives a ciphertext string that embeds the key version, and stores that ciphertext in its own database. Rotating the key creates a new version; old ciphertext still decrypts, and a rewrap call upgrades ciphertext to the latest version without ever exposing plaintext to the caller. The benefits are concrete: developers do not handle raw keys, key rotation becomes an operational task rather than a code change, and the audit log records every cryptographic operation.
The trade-off is latency and availability. Every encrypt or decrypt is a network call to a quorum-backed service, so bulk encryption of large payloads is the wrong use. For big data, use transit to generate and wrap a data key (envelope encryption), encrypt locally, and store the wrapped key beside the data. The 2.5 release added associated_data for transit data key generation, which binds a wrapped key to a context such as a tenant ID.
Namespaces for multi-tenancy
Namespaces arrived in OpenBao 2.3 as a hierarchical directory of isolated tenants, each with its own secrets engines, auth methods and policies, managed with bao namespace commands. This is the feature that matters most to platform teams serving many business units from a shared cluster, and it is one that Vault gates behind an enterprise license.

Figure 4: Namespace tree. Each tenant gets delegated administration over its own mounts and policies, and in 2.6 a tenant namespace can carry its own seal.
bao namespace create platform
bao namespace create -namespace=platform team-payments
BAO_NAMESPACE=platform/team-payments bao secrets enable -path=secret kv-v2
BAO_NAMESPACE=platform/team-payments bao policy write app payments-app.hcl
OpenBao 2.6 (14 July 2026) added namespace sealing: you can set a Shamir seal when creating a namespace, so each tenant has its own key material and can seal its namespace without affecting others. That gives regulated tenants a genuine kill switch and a cryptographic separation story for auditors. Be careful with the upgrade note attached to the same release: corrupt namespace identity groups created before v2.5.0 are removed during unseal and must be recreated, so check startup logs for “deleting corrupt group” after upgrading.
Namespaces are an isolation convenience, not a blast-radius boundary equal to separate clusters. Tenants share the same process, the same Raft log and the same performance envelope. If one tenant can generate a lease storm, everyone feels it. Use quotas, and split clusters for tenants whose risk profile demands physical separation.
Kubernetes Integration Patterns
The OpenBao documentation describes three Kubernetes offerings: an official Helm chart (Dev, Standalone, HA and External modes), the agent injector, and the CSI provider. The injector is described there as the more mature solution with a proven production record, and notes that it keeps no durable secret storage outside OpenBao. The documentation page I verified does not cover a dedicated secrets operator, so for an operator-style workflow the common approach is the External Secrets Operator, which speaks the Vault-compatible API.
Figure 3 above already shows the shared first step: the pod presents its service account token to the Kubernetes auth method, which validates it against the cluster and returns a token bound to a policy. The patterns differ in where the secret materializes afterwards.
bao auth enable kubernetes
bao write auth/kubernetes/config kubernetes_host="https://kubernetes.default.svc:443"
bao write auth/kubernetes/role/payments \
bound_service_account_names=payments \
bound_service_account_namespaces=payments-prod \
token_policies=payments-app token_ttl=1h
Agent injector. A mutating webhook adds a sidecar that authenticates, fetches secrets, renders them to files in a shared in-memory volume and renews leases. The application only reads a file. Its strengths are zero application change and automatic lease renewal; its costs are an extra container per pod and a webhook that must be highly available, since a failing webhook can block pod creation.
CSI provider. Secrets are mounted as a volume by the CSI driver at pod start. There is no sidecar, which reduces per-pod overhead, but renewal of long-lived dynamic leases is less natural, so it suits static or short-lived secrets better than database credentials that must be renewed for days.
External Secrets Operator. A controller syncs secrets into native Kubernetes Secret objects. This is the most compatible with tooling that expects a Secret, and the least aligned with OpenBao’s philosophy, because the credential now lives in etcd. If you take this route, enable etcd encryption at rest and restrict RBAC on Secrets tightly. Our secrets management architecture comparison goes deeper on that trade.
Direct API or SDK. Applications that authenticate and fetch themselves have the most control and the tightest coupling. This is justified for transit and PKI calls where the application is doing the cryptography.
A last Kubernetes note concerns workload identity itself. As clusters adopt projected pod certificates and trust bundles, there is a second route to identity that does not depend on a long-lived service account; the trade-offs are in our comparison of Kubernetes pod certificates, SPIFFE and cert-manager. Either way, bind OpenBao roles to a specific service account name and namespace, never a wildcard.
High Availability, Backup and Disaster Recovery
Raft gives you automatic leader election within a cluster. It does not give you protection against a corrupted dataset, an operator deleting a mount, or the loss of a whole region. That requires snapshots and a tested restore.
Operating the Raft cluster
Autopilot is enabled by default. New nodes join as non-voters until they sync with the leader’s Raft index and pass a stability threshold, which prevents a half-synced node from counting toward quorum. Setting that threshold too low can destabilize the cluster, so do not tune it casually. Dead-server cleanup can remove unhealthy nodes automatically, but in a five-node cluster think hard before enabling it, because automated removal combined with a network partition is how you shrink a cluster to the wrong side of a split. The integrated storage documentation also warns that adding several nodes at once without Autopilot can exceed the failure tolerance and cause quorum loss: its example is a three-node cluster adding three nodes together. Add nodes one at a time and check Raft indexes with bao status first.
bao operator raft autopilot state
bao operator raft list-peers
bao operator raft snapshot save /backup/bao-$(date +%F-%H%M).snap
Snapshots and the restore drill
A snapshot is a point-in-time copy of the Raft state, still encrypted by the barrier. Take them on a schedule from a standby or from the leader, ship them off-cluster with their own access controls, and keep the seal material needed to restore them separately and safely. A snapshot without access to the unseal or recovery path is an expensive file.
The drill matters more than the schedule. At least quarterly, restore the latest snapshot into an isolated cluster built from your normal automation, unseal it, and verify that a known secret, a policy and a PKI role are present. Time the exercise and record it, because your real recovery time objective is whatever that drill measures. The pitfall to name explicitly: all nodes in a Raft cluster share the same seal configuration, and a restore into an environment whose KMS key or transit seal is gone will not unseal.
Cross-region strategy
Vault Enterprise offers built-in performance and disaster recovery replication; OpenBao does not provide an equivalent open-source replication product. The honest options are: a stretched Raft cluster across three sites with latency you can tolerate (five voters over two or three zones, not across oceans); a warm standby cluster restored periodically from snapshots with a documented promotion runbook; or application-level separation where each region runs its own cluster with its own secrets and a pipeline that publishes shared configuration. I could not verify any 2026 replication feature in OpenBao’s release notes, so assume none and design accordingly.
For a deeper view of the horizontal read feature mentioned earlier: standby reads reduce leader load for reads, which helps latency in distant regions, but eventual consistency means a client in region B may not see a write made in region A for a brief window. Use the 2.7 consistency headers when read-your-writes matters.
Migrating from Vault and Staying Compatible
The migration story depends on how old your Vault is and what you use. OpenBao forked the last MPL-2.0 Vault code, and its CLI, API paths and concepts follow Vault’s, so existing muscle memory and most clients work. The project has since diverged deliberately, so “drop-in” is a statement about the fork point, not about today.
What carries over and what does not
Data-plane concepts carry over: KV v2, database, PKI, transit, AppRole, Kubernetes and OIDC auth, policies, leases and response wrapping. Where migration gets harder is in the shape of the data and the enterprise-only features you may have relied on.
A practical migration has four stages. First, inventory: list mounts, auth methods, policies, identity entities and the number of live leases on the source cluster, and flag anything that depends on Vault Enterprise functionality such as Sentinel policies, performance replication, or HSM-backed enterprise seals, since those have no direct open-source equivalent. Second, choose a data path: for modest clusters, a snapshot restore into a compatible OpenBao version is the simplest option, and the OpenBao project documents migration guidance; for clusters that drifted past the fork point, treat it as an export and import of KV data and a rebuild of configuration through infrastructure as code. Third, rehearse in a parallel cluster with real clients pointed at it through a canary route. Fourth, cut over by changing BAO_ADDR (or VAULT_ADDR where your clients still read it) and the DNS name, and keep the old cluster read-only for a rollback window.
I could not verify an exact list of Vault versions that restore cleanly into each OpenBao release, and that matters: check the migration documentation for your specific source version before promising a date. Also test every client library. Most Vault-compatible SDKs and tools work because the HTTP API is compatible, but the headers and paths that diverge (for example the build_date to commit_date rename in sys/seal-status and sys/version-history in 2.6, and the removal of performance_standby and last_wal from sys/health) can break monitoring that parsed them.
Where OpenBao and Vault now differ
| Capability | OpenBao (verified through 2.7) | HashiCorp Vault |
|---|---|---|
| License | MPL-2.0, Linux Foundation governance | BSL since August 2023 |
| Namespaces | Included (since 2.3) | Enterprise feature |
| Declarative self-init | Included (since 2.4) | Not verified |
| Standby read scaling | Raft since 2.5, PostgreSQL in 2.7 | Not verified for Community Edition |
| Namespace-level sealing | Included (2.6) | Not verified |
| Control groups | Included (2.7) | Enterprise historically |
| Cross-cluster replication | None in open source | Enterprise |
| Sentinel policies | None | Enterprise |
Read the right-hand column as “to my knowledge, and not independently re-verified for October 2026”, because I did not fetch Vault’s current release notes. The left-hand column is taken from the official OpenBao release notes cited in this post. A good habit before any migration decision is to diff the two projects’ current documentation yourself.
The 2.6 and 2.7 removals deserve their own mention because they are the cost of the project’s focus. Kerberos, LDAP and RADIUS auth methods and the LDAP secrets engine were deprecated in 2.6 and now ship through a separate openbao-plugins repository. The file storage backend is deprecated, with removal from bao operator migrate slated for 2.8.0. The 2.7 release notes also mention a new PebbleDB non-HA durable backend. If you rely on LDAP auth or file storage, plan that work before upgrading.
Trade-offs, Gotchas, and What Goes Wrong
The seal dependency is a hidden single point of failure. Auto-unseal makes day-to-day restarts painless and makes your cluster depend on something else at the worst moment. A disabled or deleted KMS key, a revoked IAM role or an unreachable transit-seal cluster can turn a routine node restart into an outage. Alert on key-state changes, document the manual Shamir or recovery escape hatch, and rehearse it.
Audit is fail-closed. If every enabled audit device fails to write, requests are refused. That protects you from unaudited access and also converts a full log disk into a full outage. Ship audit logs off-host, rotate them, and alert on sink latency. The 2.6.4 advisory (GHSA-q74w-hv5x-6hxf) is a reminder that this guarantee has had edge cases: unauthenticated root-token generation and rekey endpoints returned responses even when audit logging failed. Those endpoints are now disabled by default, and OpenBao 2.5 made disable_unauthed_rekey_endpoints default to true. Leave it that way.
Lease explosions. A deployment that requests credentials in a tight loop, a crashlooping pod, or a job that never revokes will pile up leases until revocation lags and the database fills with users. Use sensible TTLs, set quotas, and watch the lease count metric. Every restart of a pod using an injector re-authenticates and creates a fresh lease tree.
Writes do not scale horizontally. Standby reads help read-heavy workloads. Token creation, lease creation and dynamic credentials go through the leader and through Raft consensus on disk. Benchmark your own workload on your own storage rather than trusting any published ops-per-second figure, including mine.
Version drift in a plugin-based world. With seals, some auth methods and secrets engines moving to plugins, your upgrade now has artifacts beyond the main binary. Pin plugin versions, store them in your artifact registry, and treat plugin updates as part of the release. The 2.5 release introduced declarative plugin distribution from OCI images, with options such as plugin_auto_download and plugin_auto_register; auto-download of code at server start is convenient, and it is also a supply-chain decision that you should make deliberately and pair with signature or digest verification where available.
Secrets still leak at the edges. OpenBao protects secrets in its own custody. Once a pod reads a database password into an environment variable, that variable shows up in crash dumps, /proc, debug endpoints and logs. Prefer files in memory-backed volumes, short TTLs and applications that re-read on rotation. Related supply-chain controls such as SLSA and Sigstore provenance address the other half of the problem, which is making sure the code requesting secrets is the code you built.
Configuration secrets leak through configuration endpoints. Another 2.6.4 advisory (GHSA-3237-j65r-m5rp) fixed exposure of tls_acme_eab_mac_key through sys/config/state/sanitized. The lesson is general: restrict sys/ paths in policies, and do not hand out sudo capabilities on them casually.
Practical Recommendations
If you are starting fresh, deploy on the current 2.7 line, five voters on local NVMe across three zones, TLS 1.3, and an auto-unseal mechanism matched to where you run. Enable two audit devices from the first start, using declarative self-initialization to guarantee it. Give humans OIDC login only, give workloads platform-native identity wherever possible, and revoke the root token the same day you create it. Introduce dynamic credentials for your databases first, because that is where the security win is largest, and then move certificates onto the PKI engine with 24-hour leaf lifetimes.
If you are on Vault and considering a move, do not migrate for ideology. Migrate when you can name the benefit: removal of a licensing risk, namespaces without an enterprise contract, or a platform consolidation. Budget for testing integrations and monitoring, not just data movement. If you depend on replication or Sentinel, the answer may be to stay or to redesign.
If you already run OpenBao 2.5 or 2.6, the actionable item this month is the 2.7 upgrade path. Read the 2.7.0 and 2.7.1 notes, patch the security fixes, and test seal plugins on a standby before touching the leader.
A short production checklist:
- Five Raft voters, local SSD, TLS 1.3, memory locking, no swap, core dumps off.
- Auto-unseal tested by restarting a follower, with recovery keys stored under split custody.
- Two audit devices, shipped off-host, disk alerts in place.
- Root token revoked; humans on OIDC; admin policy protected by control groups where available.
- Kubernetes roles bound to explicit service account and namespace.
- Dynamic database credentials with
rotate-rootdone and TTLs under 24 hours. - Snapshot schedule plus a quarterly restore drill with a measured recovery time.
- Plugin binaries pinned, mirrored and verified; upgrade runbook includes a seal-plugin rehearsal.
- Lease count, seal status, Raft leader changes and audit latency graphed and alerted.
Frequently Asked Questions
What is OpenBao and how is it different from HashiCorp Vault?
OpenBao is a Linux Foundation project forked from the last MPL-2.0 version of Vault, after HashiCorp moved Vault to the Business Source License in August 2023. It keeps the same core concepts and a compatible API, but is developed independently and now has its own features, including namespaces, declarative self-initialization, namespace sealing and control groups. Some enterprise-oriented capabilities, such as replication, are not part of the open-source project.
Which OpenBao version should I run in production?
As of the GitHub releases page in October 2026, the latest is v2.7.1, published on 1 October, with v2.6.4 receiving security backports the same day. For new deployments choose the current 2.7 line, but read the breaking changes first: several seals and auth methods moved to plugins. If you must stay on 2.6, apply v2.6.4 for its security fixes and plan the 2.7 upgrade deliberately.
How does OpenBao auto-unseal work?
At startup the server asks an external trusted service to decrypt its root key, instead of requiring humans to enter Shamir shares. Options include cloud KMS, a transit seal on another cluster, a static key and KMIP. Initialization then produces recovery keys, which authorize sensitive operations but cannot unseal by themselves. From 2.7, the cloud KMS and PKCS#11 seals are no longer built in and must be installed as external plugins.
Should I use Raft integrated storage or an external backend?
Use Raft unless you have a compelling reason not to. It has no external dependencies, supports high availability and snapshots, and is the path the documentation recommends, with five servers per production cluster. Its performance is bound by disk latency and network round trips. The file backend is deprecated, and PostgreSQL support for standby reads arrived in 2.7 but depends on physical replication and ha_enabled.
Can OpenBao replace Vault in Kubernetes?
For most workloads, yes. OpenBao ships an official Helm chart, an agent injector and a CSI provider, and the External Secrets Operator can sync from it. The injector is described in the documentation as the more mature path. Verify your specific automation, since monitoring tools that parse fields renamed in 2.6 may need updating, and confirm that Enterprise-only features you used have a substitute.
Are dynamic secrets really worth the complexity?
Yes, where they fit. A unique, leased database credential that disappears in an hour turns a leaked password from a standing breach into a short window, and it gives you per-workload audit trails. The cost is lease management, connection churn and the need for applications to handle rotation. Start with databases and certificates, where the benefit is clearest, and keep static KV for third-party API keys that cannot be minted on demand.
Further Reading
- Kubernetes secrets management and the External Secrets Operator for the sync-based consumption pattern.
- SPIFFE and SPIRE workload identity for feeding platform identity into your auth methods.
- Kubernetes pod certificates versus SPIFFE and cert-manager for the certificate-based identity route.
- Card tokenization and PCI DSS vault architecture for a regulated example of vault-style design.
- OpenBao documentation and release notes and the OpenBao GitHub releases for the authoritative version history.
- Raft integrated storage internals and seal concepts in the official docs.
By Riju — about
