OpenBao
Automated installation and update script for an OpenBao secrets-manager server running inside an Alpine LXC on Proxmox.
Features
Single script, automatic mode selection:
| Context | Action |
|---|---|
| From Proxmox host, no existing OpenBao container | Detects newest Alpine template, creates LXC, installs bao + OpenRC service |
| From Proxmox host, OpenBao container already present | Reuses the existing LXC, refreshes packages, upgrades bao to the latest release |
From inside an LXC, no bao binary |
Installs OpenBao from scratch |
From inside an LXC, bao already present |
Updates the binary only (no config / data changes) |
The container is identified by hostname and the openbao tag, so it is
re-found across reruns even if the CTID was auto-allocated the first time.
Requirements
- Proxmox VE host with
pveam,pct,pvesh,jqavailable - Internet access from both the host (template download) and the LXC (binary download)
- Script must be run as root on the Proxmox host (enforced; the Web UI shell qualifies)
Usage
Full install (from Proxmox shell)
bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/openbao/install.sh)"
Re-running the exact same command later upgrades packages inside the LXC and
brings the bao binary to the latest release, without touching the config or
the raft data directory.
Customisation
Every parameter is exposed as an environment variable:
| Variable | Default | Description |
|---|---|---|
CTID |
auto | Container ID (auto-allocated via pvesh get /cluster/nextid) |
OPENBAO_HOSTNAME |
openbao |
LXC hostname (also used as raft node_id) |
TEMPLATE |
auto-detected | Alpine template; auto-detected from pveam available |
STORAGE |
local-lvm |
Proxmox storage for the LXC root disk |
TEMPLATE_STORAGE |
local |
Storage where Alpine templates live |
CORES |
2 |
vCPU cores |
RAM |
1024 |
RAM in MiB |
DISK |
8 |
Root disk size in GB |
BRIDGE |
vmbr0 |
Network bridge |
LXC_TAG |
openbao |
Stable tag used to re-discover the container |
OPENBAO_VERSION |
latest |
Pin a specific release (e.g. v2.0.3) or latest |
OPENBAO_LISTEN_ADDR |
127.0.0.1:8200 |
TCP listener address. Loopback by default — Tailscale fronts it. |
OPENBAO_API_ADDR |
http://<listen> |
Public API URL (used for UI / OIDC redirects). Set to https://<host>.<tailnet>.ts.net once known. |
TS_AUTHKEY |
(unset) | Pre-auth key (generate at https://login.tailscale.com/admin/settings/keys). If unset, finish tailscale up manually inside the LXC. |
CTID=210 OPENBAO_HOSTNAME=vault CORES=4 RAM=2048 \
bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/openbao/install.sh)"
First-time initialisation
OpenBao starts sealed. Once the LXC is up:
pct enter <CTID>
export VAULT_ADDR=http://127.0.0.1:8200
bao operator init # save the unseal keys + root token somewhere safe
bao operator unseal # repeat with each key share until unsealed
Tailscale reverse proxy
The listener binds to 127.0.0.1:8200 only — Tailscale (running inside the
same LXC) acts as the reverse proxy and terminates TLS via tailnet
certificates.
If TS_AUTHKEY was supplied at install time, the script runs
tailscale up and tailscale serve --bg --https=443 http://127.0.0.1:8200
automatically. OpenBao then becomes reachable at
https://<hostname>.<tailnet>.ts.net.
Otherwise, finish setup manually inside the LXC:
pct enter <CTID>
tailscale up --ssh
tailscale serve --bg --https=443 http://127.0.0.1:8200
tailscale status # prints the tailnet FQDN
Then point OPENBAO_API_ADDR at that FQDN and rerun the script so the UI / OIDC redirects use it:
OPENBAO_API_ADDR=https://openbao.<tailnet>.ts.net \
bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh)"
HTTPS in
tailscale serverequires HTTPS to be enabled on your tailnet (Admin console → DNS → HTTPS Certificates).
Update (from inside the LXC)
curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh | bash
The script auto-detects the presence of /usr/local/bin/bao and switches to
update mode. The OpenRC service is stopped, the binary is swapped (the old one
is kept as bao.bak.<ts>), then the service is restarted.
Architecture
- OS: latest Alpine LXC template (auto-detected), unprivileged,
nesting=1,/dev/net/tunpassthrough for Tailscale - Binary: official
baorelease fromgithub.com/openbao/openbao, installed in/usr/local/bin - Service: OpenRC, runs as user
openbao, logs to/var/log/openbao.log(rotated daily, 7 days retained) - Network: listener bound to
127.0.0.1:8200; Tailscale runs in the LXC and acts as the reverse proxy (tailscale serve --https=443) - Config:
/etc/openbao/config.hcl— raft storage, TLS disabled on the listener (Tailscale terminates TLS),disable_mlock = truefor unprivileged LXC - Data:
/var/lib/openbao/data(raft) - Version tracking:
/opt/openbao_version.txtrecords the currently installed tag for idempotent reruns