Self-Hosting OpenBao on Ubuntu: TLS, Raft Storage & Backups

Updated on Aug 31, 2026
Mila H
8 MINS READ
Table of Contents
Deploy OpenBao on Ubuntu Server

If you run Kubernetes, Docker Compose, or other self-hosted apps, you need one secure place for API keys, database credentials, and TLS keys. This guide shows how to self-host OpenBao on Ubuntu as a secrets server, with Raft storage, TLS, policies, auth methods, and backups.

What OpenBao Is and Why Dev Mode Is Not a Server

OpenBao is an open-source and Linux Foundation-governed fork of HashiCorp Vault. It stores and encrypts secrets, certificates, and credentials through the bao CLI and API, which work like the vault command.

Most tutorials use bao server -dev, which runs in memory with a fixed root token. It is fine for testing, but everything is lost on restart, and there's no TLS. For real use, you can run bao server -config=<file> with a persistent Raft directory, TLS, and a proper unseal strategy. 

Knowing this difference, dev mode vs. a real server, is the key first step before storing any real secrets.

Prerequisites to Self-Host OpenBao on Ubuntu

Before you self-host OpenBao on Ubuntu, make sure you have:

  1. Ubuntu 22.04 or 24.04 LTS with sudo access. A small VPS is enough for a single-node setup handling typical app and Kubernetes secrets. You can check PerLod's Linux VPS hosting plans for a reliable and low-latency option.
  2. A DNS record pointing to the server's public IP.

If you expect heavy Raft write traffic, multiple teams, or plan to add HA nodes later, move to a dedicated server instead of a VPS.

Step 1: Install the Latest OpenBao Release

The most reliable way to self-host OpenBao on Ubuntu with the newest stable build is to fetch the precompiled binary directly from GitHub releases. To do this, run the commands below:

Bash
version=2.6.2curl -fsSLO "https://github.com/openbao/openbao/releases/download/v${version}/openbao_${version}_linux_amd64.tar.gz"curl -fsSLO "https://github.com/openbao/openbao/releases/download/v${version}/checksums.txt" grep "openbao_${version}_linux_amd64.tar.gz$" checksums.txt | sha256sum -c -tar -xzf "openbao_${version}_linux_amd64.tar.gz"sudo install -m 0755 bao /usr/local/bin/baobao version

In the output, you should see something similar to this:

Bash
OpenBao v2.6.2 (dd9c19c37a878cf4a81b18efb8d6f0599c7da923), committed 2026-08-18T15:48:19Z

Step 2: Create a Dedicated Service Account and Directories

Don't run OpenBao as root. Create a limited system user and locked-down data folders instead:

Bash
sudo useradd --system --home /etc/openbao --shell /bin/false openbao sudo mkdir -p /etc/openbao /opt/openbao/data /opt/openbao/tls /opt/openbao/backupssudo chown -R openbao:openbao /opt/openbaosudo chmod 700 /opt/openbao/data

The /opt/openbao/data folder holds the Raft log and encrypted state. It's essentially the whole database, so its permissions matter just as much as your TLS certificates.

Step 3: Configure Raft Storage and TLS

OpenBao's Integrated Storage uses the Raft algorithm to keep a durable and replicated copy of your data on disk. This is the core configuration step for a real persistent backend instead of the in-memory dev store.

Use your favorite text editor to create the /etc/openbao/openbao.hcl file:

Bash
sudo nano /etc/openbao/openbao.hcl

Add with your actual domain:

Bash
cluster_name = "myorg-openbao"ui           = true storage "raft" {  path    = "/opt/openbao/data"  node_id = "node1"} listener "tcp" {  address       = "0.0.0.0:8200"  tls_cert_file = "/opt/openbao/tls/fullchain.pem"  tls_key_file  = "/opt/openbao/tls/privkey.pem"} api_addr     = "https://bao.example.com:8200"cluster_addr = "https://127.0.0.1:8201" default_lease_ttl = "168h"max_lease_ttl      = "720h"

Key settings:

  • cluster_addr is required with Raft storage. OpenBao needs it to coordinate nodes, even with just one.
  • Don't add a separate ha_storage block with storage "raft"; Raft can't run alongside another HA backend.
  • Handling TLS inside OpenBao itself, not just at a proxy, means secrets are never sent over plaintext, which is safer for a service built around secrecy.

Obtain a certificate with certbot and copy it into place:

Bash
sudo apt install certbot -ysudo certbot certonly --standalone -d bao.example.com sudo cp /etc/letsencrypt/live/bao.example.com/fullchain.pem /opt/openbao/tls/sudo cp /etc/letsencrypt/live/bao.example.com/privkey.pem /opt/openbao/tls/sudo chown openbao:openbao /opt/openbao/tls/*.pemsudo chmod 600 /opt/openbao/tls/*.pem

Create the following script on your server, so renewed certificates get picked up automatically:

Bash
sudo nano /etc/letsencrypt/renewal-hooks/deploy/openbao.sh
Bash
Add:
Bash
#!/bin/bashcp /etc/letsencrypt/live/bao.example.com/fullchain.pem /opt/openbao/tls/cp /etc/letsencrypt/live/bao.example.com/privkey.pem /opt/openbao/tls/chown openbao:openbao /opt/openbao/tls/*.pemchmod 600 /opt/openbao/tls/*.pemsystemctl reload openbao

Make it executable:

Bash
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/openbao.sh

Tips: If you prefer not managing certificates yourself, you can put Caddy or another reverse proxy in front, set the listener to 127.0.0.1:8200 with tls_disable = true, and let the proxy handle TLS automatically. Either way, keep port 8200 blocked from the public internet.

Step 4: Create the systemd Service

At this point, use your desired text editor to create the systemd unit file:

Bash
sudo nano /etc/systemd/system/openbao.service

Add:

Bash
[Unit]Description=OpenBao secrets managementDocumentation=https://openbao.org/docs/Requires=network-online.targetAfter=network-online.targetConditionFileNotEmpty=/etc/openbao/openbao.hcl [Service]User=openbaoGroup=openbaoProtectSystem=fullProtectHome=read-onlyPrivateTmp=yesPrivateDevices=yesSecureBits=keep-capsAmbientCapabilities=CAP_IPC_LOCKCapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCKNoNewPrivileges=yesExecStart=/usr/local/bin/bao server -config=/etc/openbao/openbao.hclExecReload=/bin/kill --signal HUP $MAINPIDKillMode=processKillSignal=SIGINTRestart=on-failureRestartSec=5TimeoutStopSec=30LimitMEMLOCK=infinity [Install]WantedBy=multi-user.target

Disable swap, then enable and start the service:

Bash
sudo swapon --showsudo swapoff -asudo sed -i '/\sswap\s/d' /etc/fstab sudo systemctl daemon-reloadsudo systemctl enable --now openbaosudo systemctl status openbaojournalctl -u openbao -f

A healthy log shows the listener starting on port 8200 and the node waiting to be unsealed. This sealed state is normal, unlike dev mode, where it unseals itself automatically.

Step 5: Initialize and Unseal

Point the CLI to your TLS endpoint and initialize the cluster. This is the most important step of self-hosting OpenBao on Ubuntu. It generates the unseal keys and root token that control access to everything else.

Bash
export BAO_ADDR="https://bao.example.com:8200" bao operator init -key-shares=5 -key-threshold=3

Save all five unseal key shares and the root token somewhere offline and encrypted. Losing them means losing access to every secret stored on the node.

OpenBao starts sealed by default. It can find its data but cannot decrypt it until you provide enough unseal keys. Use three of your five unseal keys, entering them one at a time:

Bash
bao operator unseal   # repeat 3 times with 3 different keys

Log in with the root token and confirm the node is healthy:

Bash
bao login <root-token>bao status   # should show Sealed: false, Initialized: true
Create a limited user or service token immediately, and stop using the root token for everyday tasks. Store the root token securely and use it only for emergency admin work.

Step 6: Plan Auto-Unseal Before You Go to Production

Manually entering three unseal keys after every reboot is not practical. A reboot can leave OpenBao offline until someone unseals it. Auto-unseal solves this by using a trusted external service to unseal OpenBao automatically.

Here are the auto-unseal methods you can use:

Auto-unseal method Best for
Transit seal (a second OpenBao/Vault instance) Self-hosted setups with no cloud KMS available
AWS KMS / Azure Key Vault / GCP Cloud KMS / AliCloud KMS / OCI KMS Cloud-hosted deployments already using that provider
PKCS#11 HSM Regulated environments requiring hardware-backed keys
KMIP Enterprises with an existing KMIP-compliant key manager
Static key seal Environments with explicit, pre-established trust chains

With Auto-Unseal, OpenBao gives you recovery keys instead of normal unseal keys. Recovery keys can help with tasks like regenerating the root token, but they cannot decrypt OpenBao if your KMS, HSM, or Transit server is unavailable.

For a self-hosted setup, you can run a small second OpenBao instance as a Transit auto-unseal server for your main OpenBao server.

Add the seal configuration only after choosing your Auto Unseal method. For example, a Transit seal pointed at a second instance:

Bash
seal "transit" {  address         = "https://unseal.example.com:8200"  token           = "<transit-token>"  disable_renewal = "false"  key_name        = "autounseal"  mount_path      = "transit/"}

Moving from normal unseal keys to Auto Unseal needs short downtime. Take a fresh backup first and plan it as a maintenance task.

Step 7: Enable a Secrets Engine, Policies, and Auth Methods

With the node unsealed, turn on the KV v2 engine and write a test secret:

Bash
bao secrets enable -version=2 kvbao kv put kv/myapp db_user=admin db_password='S3cur3Pass!'bao kv get kv/myapp

Write a least-privilege policy so applications never touch the root token. Create app-readonly.hcl:

Bash
sudo nano app-readonly.hcl

Add:

Bash
path "kv/data/myapp" {  capabilities = ["read"]}

Apply it and enable an auth method for machines or humans to log in with:

Bash
bao policy write app-readonly app-readonly.hcl bao auth enable approlebao write auth/approle/role/myapp token_policies="app-readonly" \  token_ttl=1h token_max_ttl=4h bao read auth/approle/role/myapp/role-idbao write -f auth/approle/role/myapp/secret-id

For human access, use userpass or OIDC/JWT login instead of sharing static tokens. Policies and auth methods let Docker Compose, K3s, and ArgoCD access only the secrets they need without hardcoding credentials.

Step 8: Automate Snapshot Backups

Raft stores all OpenBao data, so a Raft snapshot is a full server backup. You do not need to back up a separate database. Start by creating a manual snapshot:

Bash
bao operator raft snapshot save /opt/openbao/backups/bao-$(date +%F).snap

Create a backup token that can only manage snapshots; never use the root token. Then use cron to run the backup every day:

Bash
sudo crontab -e

Add:

Bash
0 3 * * * BAO_ADDR="https://bao.example.com:8200" BAO_TOKEN="<backup-token>" \  /usr/local/bin/bao operator raft snapshot save \  /opt/openbao/backups/bao-$(date +\%F).snap

Copy .snap backups to a different location, such as S3 storage, another server, or a backup VPS. A backup on the same server is useless if that server or disk fails. Before storing real secrets, make sure you have verified off-server backups.

Step 9: Verify Recovery From a Snapshot

A backup is only reliable if you have tested restoring it. Test recovery on the same server or on a separate test server:

Bash
sudo systemctl stop openbao # Simulate data losssudo mv /opt/openbao/data /opt/openbao/data.baksudo mkdir /opt/openbao/datasudo chown openbao:openbao /opt/openbao/datasudo chmod 700 /opt/openbao/data sudo systemctl start openbao

With an empty Raft data folder, OpenBao starts as a new and uninitialized server. Initialize and unseal it first, then restore your snapshot:

Bash
bao operator init -key-shares=5 -key-threshold=3bao operator unseal   # x3bao login <new-root-token> bao operator raft snapshot restore /opt/openbao/backups/bao-2026-08-27.snap

After the restore completes, confirm your earlier secret and policies came back exactly as they were:

Bash
bao kv get kv/myappbao policy read app-readonly

If both commands show the expected data, your backup and recovery process works. This proves you can restore OpenBao successfully after a failure.

Conclusion

You now have OpenBao running with Raft storage, TLS, limited-access policies, and tested backups. Monitor it, keep off-server backups, install updates, and plan Auto Unseal before an unexpected reboot.

We hope you enjoy this guide. 

For more details about Raft storage, backups, and cluster management, see the official OpenBao documentation.

Mostly, yes. OpenBao is a Linux Foundation fork of Vault, and its bao CLI and API work similarly, so most Vault setups need only small changes.

No. One node with Raft storage is enough for most self-hosted setups. You can add more nodes later for high availability.

No. Dev mode stores everything in memory, uses no TLS, and resets on every restart. It's only for local testing, never for real secrets.