Bootstrap the cluster declaratively and avoid carrying a root token forward.
Self-initialization lets the cluster bring up auth methods, policies, audit devices, and other bootstrap state as part of the OpenBaoCluster manifest. It is the supported production bootstrap path because it avoids leaving a long-lived root token in a Kubernetes Secret.
Decision matrix
Choose the bootstrap path deliberately
| Path | Use it when | What happens to the root token | Watch for |
|---|---|---|---|
| Self-initialization | You want declarative bootstrap and a production-ready baseline. | The root token is auto-revoked after the requests complete successfully. | You must define at least one usable auth path for humans or automation before the cluster comes up. |
| Standard init | You need a temporary compatibility path for development or controlled manual setup. | A root token can be created and stored in a Secret. | This is easier to start with, but it leaves you with a stronger credential-management burden afterward. |
Diagram
Self-init bootstrap flow
The cluster initializes, applies the declared requests, and then revokes the bootstrap credential instead of treating it as a permanent operating dependency.
Self-init is safer only if you plan the access path up front. If you enable it without creating a usable auth method for operators or humans, the root token is revoked and the cluster can become effectively unreachable without recreation.
Decision matrix
Bootstrap both access surfaces together
| Access surface | Where it lives | What must be true before first reconcile |
|---|---|---|
| Operator lifecycle auth | spec.selfInit.oidc.enabled | Enable it when you want the operator to bootstrap JWT auth for backup, restore, and upgrade work. |
| Human login path | spec.selfInit.requests | Create at least one usable auth method and policy path for people before the root token is revoked. |
Do not think of operator auth as step one and human auth as something to add later.
If the cluster will self-initialize, define the human login path in selfInit.requests as part of the same manifest that enables self-init.
Enable self-init
Configure
Start from the minimum self-init block
spec:
selfInit:
enabled: true
requests:
- name: enable-audit
operation: update
path: sys/audit/file
auditDevice:
type: file
fileOptions:
filePath: /tmp/audit.log
Treat requests as part of the bootstrap contract. They should create the minimum auth, policy, and audit state required for the cluster to be useful after bootstrap.
Configure
Pair operator OIDC bootstrap with a human auth path
spec:
selfInit:
enabled: true
oidc:
enabled: true
requests:
- name: enable-userpass-auth
operation: update
path: sys/auth/userpass
authMethod:
type: userpass
- name: create-admin-policy
operation: update
path: sys/policies/acl/admin
policy:
policy: |
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# Add your user, JWT role, or Kubernetes auth role here so a real human
# login path exists before the root token is revoked.
The exact human auth method is your choice, but it belongs inside the same selfInit contract. For a complete worked example, see the development self-init userpass recipe.
What belongs in requests
Reference table
Structured request surfaces
| Surface | Use it for | Typical example |
|---|---|---|
authMethod | Enable and configure auth backends. | JWT or Kubernetes auth for operators and clients. |
policy | Create ACL policies that your auth methods will bind to. | Policies for apps, operators, or bootstrap-only roles. |
secretEngine | Enable mounts such as KV or transit. | Initial application secret storage or cryptography services. |
auditDevice | Turn on audit logging at bootstrap time. | File or stdout audit devices required by your environment. |
data | Fallback for raw API payloads when no structured field exists. | Specialized configuration that is not covered by the higher-level request fields yet. |
Avoid placing passwords, tokens, or key material directly in the manifest. Use Kubernetes Secrets where supported and treat bootstrap content like the rest of your GitOps security surface.
Bootstrap operator OIDC roles
Configure
Enable operator OIDC bootstrap
spec:
selfInit:
enabled: true
oidc:
enabled: true
# Optional:
# issuer: "https://..."
# audience: "openbao-internal"
This bootstraps operator-only JWT auth roles for lifecycle work such as backup, upgrade, and restore. It does not create human login paths by itself.
Reference table
What must stay aligned
| Surface | Why it matters | What to align |
|---|---|---|
| OIDC issuer and JWKS discovery | The operator must discover the Kubernetes issuer and keys to bootstrap JWT auth cleanly. | Ensure the operator ServiceAccount can GET the OIDC discovery and JWKS non-resource URLs. |
| JWT audience | The role binding inside OpenBao and the projected token audience must match. | Keep spec.selfInit.oidc.audience aligned with the installation-scoped OPENBAO_JWT_AUDIENCE value. |
| Rendered controller identity | Custom namespace or name-prefix changes affect the ServiceAccount subject the JWT role expects. | If you manage roles manually, bind them to the rendered controller ServiceAccount identity rather than to a guessed default. |
Common bootstrap patterns
- Auth method
- Policy
- Secret engine
- Audit device
Configure
Enable a JWT auth method
- name: enable-jwt
operation: update
path: sys/auth/jwt-operator
authMethod:
type: jwt
description: "Kubernetes JWT auth"
config:
default_lease_ttl: "1h"
max_lease_ttl: "24h"
Configure
Create an ACL policy at bootstrap
- name: app-policy
operation: update
path: sys/policies/acl/app-policy
policy:
policy: |
path "secret/data/app/*" {
capabilities = ["read", "list"]
}
Configure
Enable a KV v2 mount
- name: enable-kv-v2
operation: update
path: sys/mounts/secret
secretEngine:
type: kv
description: "General purpose KV store"
options:
version: "2"
Configure
Enable audit logging at bootstrap
- name: enable-file-audit
operation: update
path: sys/audit/file
auditDevice:
type: file
fileOptions:
filePath: /var/log/openbao/audit.log
Verify the cluster finished bootstrap
Verify
Check the self-init status bit
kubectl get openbaocluster <name> -o jsonpath='{.status.selfInitialized}'
A healthy bootstrap should report true. If it does not, inspect the cluster status conditions and controller logs before retrying with additional requests.
Continue cluster baseline
You are reading docs for version 0.1.0. Use the version menu to switch to next or another archived release.
Was this page helpful?
Use Needs work to open a structured GitHub issue for this page. The Yes button only acknowledges the signal locally.