How to Use Reloader with OpenBao CSI Driver Pattern#
This guide demonstrates integrating OpenBao with Stakater Reloader using the Secrets Store CSI Driver with the OpenBao CSI Provider.
Overview#
The CSI (Container Storage Interface) pattern mounts secrets from OpenBao directly into pods as volume files. Using the secretObjects feature, secrets are also synced to Kubernetes Secrets, enabling Reloader integration.
sequenceDiagram
actor Ops as Operator
participant OB as OpenBao
participant CSI as CSI Driver +<br/>OpenBao Provider
participant K8s as Kubernetes Secret
participant RL as Reloader
participant Pod as Application Pod
Note over CSI: Authenticates via Kubernetes Auth
CSI->>OB: Authenticate (K8s SA JWT)
OB-->>CSI: Auth token
Note over Pod: Pod has CSI volume mounted
Ops->>OB: Rotate secret (bao kv put)
loop CSI rotation interval
CSI->>OB: Fetch secrets
OB-->>CSI: Updated values
CSI->>Pod: Refresh mounted files
CSI->>K8s: Sync to Secret via secretObjects
end
K8s-->>RL: Watch event (Secret changed)
RL->>Pod: Trigger rolling restart
Note over Pod: New pod starts with updated secret
Prerequisites#
Complete the common setup steps from the Overview:
- OpenBao installed, initialized, and unsealed
- OpenBao CLI configured (
BAO_ADDRandBAO_TOKENexported) - KV v2 secrets engine enabled
- Test secrets written to
secret/myapp - Stakater Reloader installed with search mode enabled
Additional requirements:
- Secrets Store CSI Driver installed with
syncSecretand rotation enabled - OpenBao CSI Provider installed (included in OpenBao Helm chart)
Note: All
baocommands below assumeBAO_ADDRandBAO_TOKENare set from Overview Step 3.
Step 1: Install Secrets Store CSI Driver#
Install with syncSecret and rotation enabled:
helm repo add secrets-store-csi-driver \
https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo update
helm install csi-secrets-store \
secrets-store-csi-driver/secrets-store-csi-driver \
-n kube-system \
--set syncSecret.enabled=true \
--set enableSecretRotation=true \
--set rotationPollInterval=30s
Step 2: Enable OpenBao CSI Provider#
If not already enabled, upgrade OpenBao installation:
helm upgrade openbao openbao/openbao \
-n openbao \
--set csi.enabled=true \
--reuse-values
Verify the CSI provider is running:
kubectl get pods -n openbao -l app.kubernetes.io/name=openbao-csi-provider
Step 3: Configure OpenBao#
Create Read Policy#
bao policy write myapp-read - <<EOF
path "secret/data/myapp" {
capabilities = ["read"]
}
EOF
Enable Kubernetes Auth#
bao auth enable kubernetes
bao write auth/kubernetes/config \
kubernetes_host='https://kubernetes.default.svc.cluster.local'
Create Auth Role#
bao write auth/kubernetes/role/openbao-csi-role \
bound_service_account_names=openbao-csi-sa \
bound_service_account_namespaces=openbao-csi-test \
policies=myapp-read \
ttl=1h
Write Test Secret#
bao kv put secret/myapp username=myuser password=mypassword
Step 4: Create Application Namespace and ServiceAccount#
kubectl create namespace openbao-csi-test
# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: openbao-csi-sa
namespace: openbao-csi-test
Step 5: Create SecretProviderClass#
The secretObjects section syncs secrets to a Kubernetes Secret with the Reloader annotation:
# secret-provider-class.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: openbao-secrets
namespace: openbao-csi-test
spec:
provider: openbao
secretObjects:
- secretName: app-secrets
type: Opaque
annotations:
reloader.stakater.com/match: "true"
data:
- objectName: username
key: username
- objectName: password
key: password
parameters:
vaultAddress: "http://openbao.openbao.svc.cluster.local:8200"
roleName: openbao-csi-role
objects: |
- objectName: "username"
secretPath: "secret/data/myapp"
secretKey: "username"
- objectName: "password"
secretPath: "secret/data/myapp"
secretKey: "password"
Key Configuration:
provider: openbao- Uses OpenBao CSI provider (not "Vault")secretObjects- Syncs mounted secrets to Kubernetes Secretannotations- Reloader annotation for automatic restartsparameters.vaultAddress- Points to OpenBao serviceparameters.roleName- Kubernetes auth role name
Step 6: Deploy Application#
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: openbao-csi-test-app
namespace: openbao-csi-test
annotations:
reloader.stakater.com/search: "true"
spec:
replicas: 1
selector:
matchLabels:
app: openbao-csi-test-app
template:
metadata:
labels:
app: openbao-csi-test-app
spec:
serviceAccountName: openbao-csi-sa
containers:
- name: app
image: busybox:latest
command:
- "sh"
- "-c"
- |
while true; do
echo "Username: $APP_USERNAME"
echo "Password: $APP_PASSWORD"
sleep 30
done
env:
- name: APP_USERNAME
valueFrom:
secretKeyRef:
name: app-secrets
key: username
- name: APP_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: password
volumeMounts:
- name: secrets-store
mountPath: "/mnt/secrets"
readOnly: true
volumes:
- name: secrets-store
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: openbao-secrets
Key Configuration:
reloader.stakater.com/search: "true"- Enables Reloader search modeserviceAccountName- Must match the auth role bindingvolumeMounts- Mounts secrets as files (optional but required for sync)env- References the synced Kubernetes Secret
Step 7: Verify Setup#
Apply manifests:
kubectl apply -f serviceaccount.yaml
kubectl apply -f secret-provider-class.yaml
kubectl apply -f deployment.yaml
Check pod is running:
kubectl get pods -n openbao-csi-test
Verify synced secret:
kubectl get secret app-secrets -n openbao-csi-test -o yaml
Check pod logs:
kubectl logs -n openbao-csi-test -l app=openbao-csi-test-app
Step 8: Test Secret Rotation#
Record current pod:
kubectl get pods -n openbao-csi-test -l app=openbao-csi-test-app
Update secret in OpenBao:
bao kv put secret/myapp username=myuser password=rotated-password
Wait for CSI rotation (based on rotationPollInterval) and Reloader:
# After ~30–60 seconds
kubectl get pods -n openbao-csi-test -l app=openbao-csi-test-app
kubectl logs -n openbao-csi-test -l app=openbao-csi-test-app --tail=5
The pod should show a new name and the updated password.
How It Works#
- CSI Driver mounts secrets - When the pod starts, the CSI driver authenticates to OpenBao using Kubernetes auth and mounts secrets as files
secretObjectssync - The CSI driver creates/updates the Kubernetes Secret with the Reloader annotation- Rotation polling - CSI driver periodically polls OpenBao for changes (based on rotationPollInterval)
- Secret update - When secrets change, CSI driver updates the Kubernetes Secret
- Reloader triggers restart - Reloader detects the Secret change and performs a rolling restart of the Deployment
Important Notes#
Provider Name#
The OpenBao CSI provider uses openbao as the provider name, not vault:
spec:
provider: openbao # NOT "vault"
Rotation Settings#
The CSI driver must have rotation enabled:
--set enableSecretRotation=true
--set rotationPollInterval=30s
Volume Mount Required#
The CSI volume must be mounted in the pod for secretObjects to work. The Kubernetes Secret is only created/updated when the pod is running with the volume mounted.
Authentication#
CSI uses Kubernetes auth with the pod's ServiceAccount. The ServiceAccount must be bound to an OpenBao role with appropriate policies.
Troubleshooting#
Provider Not Found#
provider not found: provider "openbao"
Ensure OpenBao CSI provider is installed:
kubectl get pods -n openbao -l app.kubernetes.io/name=openbao-csi-provider
Secret Not Syncing#
- Check CSI driver has
syncSecretenabled - Verify pod is running with the CSI volume mounted
- Check
secretObjectsconfiguration matchesparameters.objects
Authentication Errors#
- Verify ServiceAccount exists and is used by the pod
- Check OpenBao role bindings match the namespace and ServiceAccount
- Review OpenBao CSI provider logs:
kubectl logs -n openbao -l app.kubernetes.io/name=openbao-csi-provider -c openbao-csi-provider