Install the operator in the mode you actually intend to run.
Choose a supported install path, keep the rendered namespace and identity explicit, and verify the controller wiring before you create your first OpenBaoCluster.
Preflight before you install
- confirm Kubernetes compatibility and cluster-admin access for CRDs, RBAC, and admission policies
- decide whether Helm or raw manifests own the install lifecycle
- decide whether you are staying multi-tenant or intentionally switching to single-tenant mode
- if you stay multi-tenant, know who will create the first OpenBaoTenant and in which namespace
- pin a released operator version for production instead of relying on floating tags
Prerequisites
- Kubernetes: v1.33+ (see Compatibility)
- kubectl: Installed and configured
- Permissions: Cluster-admin access for CRDs, RBAC, and ValidatingAdmissionPolicies
- Helm (optional): v3.12+ for Helm-based installation
The operator supports two deployment modes:
- Multi-Tenant (default): Platform teams providing OpenBao-as-a-Service
- Single-Tenant: Individual teams deploying OpenBao for their application
See Single-Tenant Mode for single-tenant deployments.
Install Profiles
Use this table to choose the supported install path before you start changing values or overlays. For most environments, the default answer is Helm plus multi-tenant mode unless your namespace ownership model says otherwise.
Decision matrix
Supported installation paths
| Intent | Recommended path | Change these settings | Verify these outputs |
|---|---|---|---|
| Default shared production install | Helm, multi-tenant mode | release namespace, chart version, controller/provisioner sizing | controller and provisioner pods in the rendered operator namespace |
| Dedicated team namespace | Helm, tenancy.mode=single | tenancy.targetNamespace, optional release namespace | only the controller pod runs; WATCH_NAMESPACE matches the target namespace |
| Dedicated team namespace with custom Helm identity | Helm, tenancy.mode=single plus custom release name or fullnameOverride | release name or fullnameOverride, tenancy.targetNamespace, optional release namespace | rendered controller ServiceAccount name, single-tenant RoleBinding subject, admission-policy identity variables, JWT audience |
| Raw multi-tenant install with default identity | config/default | operator namespace only if you want to fork the default base | rendered namespace, controller and provisioner ServiceAccount names, admission policies |
| Raw multi-tenant install with custom identity | config/overlays/custom-identity | namespace, optional namePrefix | rendered ServiceAccount names, RoleBinding subjects, admission-policy identity variables, JWT audience |
| Raw single-tenant install | config/overlays/single-tenant | operator namespace in the overlay, target namespace in target_namespace_config.yaml | rendered operator namespace, WATCH_NAMESPACE, single-tenant RoleBinding subject |
| Raw single-tenant install with custom identity | config/overlays/single-tenant-custom-identity | namespace, optional namePrefix, target namespace in target_namespace_config.yaml | rendered operator namespace, controller ServiceAccount name, WATCH_NAMESPACE, single-tenant RoleBinding subject, admission-policy identity variables |
Use config/overlays/single-tenant when you only need a custom operator namespace or target namespace.
Use config/overlays/single-tenant-custom-identity when you also need a custom operator identity, such as an extra namePrefix.
Start with Helm, keep the default multi-tenant mode, pin the chart release for production, and leave admission policies enabled. Deviate from that path only when raw-manifest control or single-tenant namespace ownership is an explicit requirement.
Installation
- Helm (Recommended)
- OpenShift
- YAML Manifests
- Developer (Source)
Install the operator using the official Helm chart. For production, pin the chart release explicitly with --version.
The examples below use the default release namespace openbao-operator-system. If you install the chart into another namespace, replace it consistently in the commands and later verification steps.
Apply
Install the Helm chart
helm upgrade --install openbao-operator oci://ghcr.io/dc-tec/charts/openbao-operator \
--version <chart-version> \
--namespace openbao-operator-system \
--create-namespace
Common Configuration
Configure
Pin the chart release and right-size the controller
helm upgrade --install openbao-operator oci://ghcr.io/dc-tec/charts/openbao-operator \
--version <chart-version> \
--namespace openbao-operator-system \
--create-namespace \
--set controller.replicas=2 \
--set controller.resources.limits.memory=512Mi
- Pin the chart release with
--versionfor production deployments. - Run multiple replicas for high availability.
- Adjust resource limits based on cluster size.
For normal installs, pin the chart with --version and let the chart's appVersion select the matching operator image.
Use image.tag only when you intentionally need a non-default operator image for that chart, such as prerelease validation or a controlled override.
Single-Tenant With Custom Helm Identity
Helm already supports the equivalent of the raw-manifest custom-identity overlays through the release name and fullnameOverride.
Configure
Install in single-tenant mode with a custom identity
helm upgrade --install team-bao oci://ghcr.io/dc-tec/charts/openbao-operator \
--version <chart-version> \
--namespace platform-operators \
--create-namespace \
--set tenancy.mode=single \
--set tenancy.targetNamespace=openbao \
--set fullnameOverride=team-bao-operator
Confirm with helm template or helm get manifest that:
- the controller
ServiceAccountname matches the rendered Helm fullname - the single-tenant
RoleBindingsubject points at that rendered controllerServiceAccount - admission-policy variables reference the same rendered operator namespace and controller
ServiceAccountname OPENBAO_JWT_AUDIENCEon the controller still matches the projectedopenbao-tokenaudience
The chart does not expose per-component custom ServiceAccount names. Use the release name or fullnameOverride to customize the operator identity while keeping the rendered RBAC and admission-policy references aligned.
Artifact Hub
Discover package metadata and install snippets on Artifact Hub:
Artifact Hub indexing can lag shortly after a release is published.
Full Values Reference
| Parameter | Description | Default |
|---|---|---|
image.repository | Operator image repository | ghcr.io/dc-tec/openbao-operator |
image.tag | Image tag (defaults to appVersion) | "" |
image.pullPolicy | Image pull policy | IfNotPresent |
imagePullSecrets | Registry credentials | [] |
platform | Target platform (auto, kubernetes, openshift) | auto |
tenancy.mode | multi or single | multi |
tenancy.targetNamespace | Target namespace (single-tenant only) | "" |
controller.replicas | Controller replica count | 1 |
controller.resources | Controller resource requests/limits | See values.yaml |
provisioner.replicas | Provisioner replica count | 1 |
provisioner.resources | Provisioner resource requests/limits | See values.yaml |
admissionPolicies.enabled | Enable ValidatingAdmissionPolicies | true |
metrics.enabled | Enable metrics endpoints | true |
To use private registries for the operator and its sidecars (init, backup, upgrade), see the Air-Gapped / Private Registries guide.
For Red Hat OpenShift clusters, the operator defaults to platform auto-detection. You can optionally force the platform mode to ensure compatibility with Security Context Constraints (SCC):
Configure
Force OpenShift platform mode
helm upgrade --install openbao-operator oci://ghcr.io/dc-tec/charts/openbao-operator \
--version <chart-version> \
--namespace openbao-operator-system \
--create-namespace \
--set platform=openshift
This setting instructs the chart/operator to omit pinned runAsUser / fsGroup IDs in generated Pods, allowing OpenShift's SCC admission controller to inject namespace-scoped IDs automatically.
Apply the installer manifest directly from a pinned GitHub Release:
Apply
Apply the pinned installer manifest
kubectl apply -f https://github.com/dc-tec/openbao-operator/releases/download/X.Y.Z/install.yaml
Replace X.Y.Z with the exact release you intend to run. Use latest only for throwaway evaluation, not for production installs.
Raw-manifest installs have three supported starting points:
config/default: default multi-tenant installconfig/overlays/custom-identity: multi-tenant install with custom operator namespace ornamePrefixconfig/overlays/single-tenant: direct single-tenant install without the provisionerconfig/overlays/single-tenant-custom-identity: direct single-tenant install without the provisioner plus custom operator identity support
For raw-manifest installs with a custom operator namespace or extra name prefix, start from config/overlays/custom-identity. Set namespace there and optionally add namePrefix. The controller and provisioner ServiceAccount identities, RoleBinding subjects, and admission-policy identity checks follow the installed ServiceAccounts automatically.
For direct single-tenant installs, start from config/overlays/single-tenant. That overlay owns the operator namespace and target namespace wiring instead of relying on manual WATCH_NAMESPACE patches.
If you need single-tenant mode and a custom operator identity, such as an extra namePrefix, start from config/overlays/single-tenant-custom-identity. That overlay keeps the single-tenant namespace wiring and the controller admission-policy identity rewrites aligned in one supported path.
If you use custom raw-manifest identities together with manual OpenBao JWT configuration or self-init OIDC bootstrap, verify the rendered controller ServiceAccount name and namespace first. See Operator Authentication.
For local development and contribution:
Apply
Install from source for development
# Install CRDs
make install
# Deploy operator (uses Kustomize)
make deploy IMG=ghcr.io/dc-tec/openbao-operator:dev
Render Verification
Use this checklist for raw-manifest installs before you apply the manifests.
Multi-Tenant With Custom Identity
Render the overlay:
Inspect
Render the custom-identity overlay
kubectl kustomize config/overlays/custom-identity
Confirm:
- the rendered operator namespace is the namespace you expect
- the controller and provisioner
ServiceAccountnames match your intended install identity RoleBindingandClusterRoleBindingsubjects point at those rendered ServiceAccounts- admission-policy variables reference the same rendered namespace and ServiceAccount names
OPENBAO_JWT_AUDIENCEon the controller matches the projectedopenbao-tokenaudience
See Operator Authentication for the OpenBao-side JWT binding checks.
Single-Tenant Raw Manifests
Render the overlay:
Inspect
Render the single-tenant overlay
kubectl kustomize config/overlays/single-tenant
Confirm:
- the rendered operator namespace matches
config/overlays/single-tenant/kustomization.yaml WATCH_NAMESPACEon the controller matchesconfig/overlays/single-tenant/target_namespace_config.yaml- the single-tenant
RoleBindingnamespace matches the same target namespace - the controller
ServiceAccountsubject in thatRoleBindingpoints at the rendered operator namespace
If you customize the single-tenant overlay beyond those supported fields, treat the render output as the source of truth.
Single-Tenant With Custom Identity
Render the overlay:
Inspect
Render the single-tenant custom-identity overlay
kubectl kustomize config/overlays/single-tenant-custom-identity
Confirm:
- the rendered operator namespace matches
config/overlays/single-tenant-custom-identity/kustomization.yaml - the rendered controller
ServiceAccountname matches the same overlay after anynamePrefix WATCH_NAMESPACEon the controller matchesconfig/overlays/single-tenant-custom-identity/target_namespace_config.yaml- the single-tenant
RoleBindingsubject points at the rendered controllerServiceAccount - controller admission-policy variables reference the same rendered namespace and
ServiceAccountname
Verify Installation
Check that the operator pods are running:
Verify
Check that the operator pods are running
kubectl get pods -n <operator-namespace>
Expected output (multi-tenant mode):
Output
Expected output in multi-tenant mode
NAME READY STATUS RESTARTS AGE
<controller-pod> 1/1 Running 0 1m
<provisioner-pod> 1/1 Running 0 1m
A good install checkpoint is more than pods in Running:
- the controller and provisioner pods match the tenancy mode you chose
- the rendered namespace and ServiceAccount names match your install plan
- admission policies are installed when they are supposed to be
- in the default multi-tenant path, you know which namespace will receive the first
OpenBaoTenant - your next step is tenant onboarding or cluster creation, not more install debugging
Upgrading
Helm Upgrades
Helm does not automatically upgrade CRDs. For releases with CRD changes:
- Apply CRDs from the release assets first:
kubectl apply -f https://github.com/dc-tec/openbao-operator/releases/download/X.Y.Z/crds.yaml - Then upgrade the Helm release:
helm upgrade openbao-operator oci://ghcr.io/dc-tec/charts/openbao-operator \
--version X.Y.Z \
--namespace openbao-operator-system
YAML Manifest Upgrades
kubectl apply -f https://github.com/dc-tec/openbao-operator/releases/download/X.Y.Z/install.yaml
For custom raw-manifest installs, use your rendered namespace, ServiceAccount names, and policy names rather than assuming the repository defaults.
Uninstallation
- Helm
- YAML Manifests
helm uninstall openbao-operator --namespace openbao-operator-system
Helm does not delete CRDs by design. To fully remove:
kubectl delete crd openbaoclusters.openbao.org openbaorestores.openbao.org openbaotenants.openbao.org
kubectl delete -f https://github.com/dc-tec/openbao-operator/releases/download/X.Y.Z/install.yaml
Next Steps
Next actions
You are reading the unreleased main docs. Use the version menu for the newest published release, or check the release notes for what is already out.
Was this page helpful?
Use Needs work to open a structured GitHub issue for this page. The Yes button only acknowledges the signal locally.