security

Syncing Secrets into Kubernetes with the External Secrets Operator

Abubakar Siddiq Ango
Abubakar Siddiq Ango Senior Developer Advocate
Jun 17, 2026 3 min read Intermediate
security secrets-management openbao external-secrets

Prerequisites

  • Completed ‘Storing and Reading Secrets with OpenBao’ (part 3) — OpenBao holds a secret at secret/myapp/config
  • kubectl and Helm 3 installed

Introduction

Your secret lives in OpenBao. Now you will deliver it to the cluster as an ordinary Kubernetes Secret, kept in sync by the External Secrets Operator (ESO). Your manifests reference a path in OpenBao; the secret value never appears in them.

ESO works through two resources: a SecretStore that says how to reach a backend, and an ExternalSecret that says which values to pull and what Kubernetes Secret to write.

Step 1 — Install the External Secrets Operator

helm repo add external-secrets https://charts.external-secrets.io
helm repo update

helm install external-secrets external-secrets/external-secrets \
  -n external-secrets-system --create-namespace --wait

Confirm the operator is running:

kubectl get pods -n external-secrets-system
NAME                                                READY   STATUS    RESTARTS   AGE
external-secrets-8977b889d-vt4sq                    1/1     Running   0          80s
external-secrets-cert-controller-5d4dc587db-pkckx   1/1     Running   0          80s
external-secrets-webhook-85d855b54-4w9hq            1/1     Running   0          80s

Step 2 — Give ESO a token and a SecretStore

ESO authenticates to OpenBao with a token. OpenBao is Vault-compatible, so you use ESO’s vault provider. Create a namespace, a Kubernetes Secret holding the OpenBao token (root in dev mode, base64-encoded as cm9vdA==), and a SecretStore pointing at the OpenBao service:

kubectl create namespace demo-app

kubectl apply -f- <<'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: openbao-token
  namespace: demo-app
data:
  token: cm9vdA==   # base64("root")
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: openbao-backend
  namespace: demo-app
spec:
  provider:
    vault:
      server: "http://openbao.openbao.svc:8200"
      path: "secret"
      version: "v2"
      auth:
        tokenSecretRef:
          name: "openbao-token"
          key: "token"
EOF

In production you would give ESO a scoped token from a dedicated policy, with read access to only the paths it needs.

Step 3 — Create an ExternalSecret

The ExternalSecret names the source path and the keys to pull, and the target Secret to create:

kubectl apply -f- <<'EOF'
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: myapp-config
  namespace: demo-app
spec:
  refreshInterval: "15s"
  secretStoreRef:
    name: openbao-backend
    kind: SecretStore
  target:
    name: myapp-config
  data:
    - secretKey: api_key
      remoteRef:
        key: myapp/config
        property: api_key
    - secretKey: db_password
      remoteRef:
        key: myapp/config
        property: db_password
EOF

Step 4 — Verify the synced Secret

Check that the ExternalSecret synced:

kubectl -n demo-app get externalsecret myapp-config
NAME           STORE             REFRESH INTERVAL   STATUS         READY
myapp-config   openbao-backend   15s                SecretSynced   True

ESO created a native Kubernetes Secret with the values from OpenBao:

kubectl -n demo-app get secret myapp-config -o jsonpath='{.data.api_key}' | base64 -d
s3cr3t-123

The value matches what you stored in OpenBao, and it never appeared in a manifest.

Step 5 — Use it in a workload

myapp-config is an ordinary Secret, so a Pod consumes it the usual way:

    envFrom:
      - secretRef:
          name: myapp-config

When you rotate the value in OpenBao, ESO refreshes the Secret on its refreshInterval, and your workload picks up the new value on its next restart.

Clean up

kubectl delete namespace demo-app
helm uninstall external-secrets -n external-secrets-system

What’s next

You now have the open-source foundation: OpenBao storing secrets and ESO syncing them into Kubernetes. From here you can add per-path policies, dynamic secrets, and audit logging — and run the whole thing as a managed, multi-tenant platform with Kubermatic SecureGuard.

Summary

  • The External Secrets Operator syncs secrets from a backend into native Kubernetes Secret objects.
  • A SecretStore defines how to reach OpenBao (the Vault-compatible provider, a token, the KV path and version).
  • An ExternalSecret names the source path and keys and the target Secret to write.
  • Workloads consume the result as a normal Secret; rotation in OpenBao flows through on the refresh interval.