In a zero-trust cloud architecture, keeping your application secrets out of version control is non-negotiable. If you took the advice from our previous article on moving secrets to HashiCorp Vault, you successfully secured your sensitive tokens away from your source code.
But this introduces a new practical problem: How do those tokens actually get inside your running Kubernetes containers?
The manual approach is a major operational trap. A developer fetches the key from Vault, runs kubectl create secret generic, encodes the values in base64, and pushes them manually to the cluster. This method is slow, breaks continuous delivery pipelines, and introduces human error. Even worse, if you rotate the key in Vault, someone has to remember to manually repeat the entire process via the command line.
The professional solution is to automate this handshake using the External Secrets Operator (ESO). Here is how to configure an automated, self-healing secrets bridge inside your Kubernetes cluster.
The Operator Strategy: Continuous Synchronization
Instead of pushing secrets manually, the External Secrets Operator runs as an intelligent controller inside your cluster. It continuously polls your external Vault instance, fetches the targeted key-value sets, and translates them into standard native Kubernetes Secrets out of thin air.
┌──────────────────────┐
│ HashiCorp Vault │ (External System of Record)
└──────────┬───────────┘
│
│ 1. Continually Polls (e.g., every 10s)
▼
┌──────────────────────┐
│ External Secrets │
│ Operator (ESO) │ (In-Cluster Controller Daemon)
└──────────┬───────────┘
│
│ 2. Dynamically generates/updates
▼
┌──────────────────────┐
│ Native K8s Secret │ ──► Mounted to ──► [ Application Pods ]
└──────────────────────┘
🛠️ Step-by-Step Implementation
Following our encapsulated repository pattern, we deploy the operator mesh using Helm and declare our secret targets using native custom resources.
1. Bootstrapping the Operator Stack
Run the following commands to install the core operator components inside your cluster:
# Add the official External Secrets Helm repository
helm repo add external-secrets https://external-secrets.io
helm repo update
# Install the operator controllers into an isolated namespace
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace
2. Linking the Vault Backend (templates/secret-store.yaml)
Create a manifest file inside your Helm chart configuration folder to tell the operator how to securely communicate with the target Vault endpoint:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "http://cluster.local" # Internal Vault DNS address
path: "secret"
version: "v2"
auth:
tokenSecretRef:
name: "vault-token" # Points to the cluster token secret
key: "token"
3. Creating the Dynamic Injection Sync (templates/external-secret.yaml)
Next, define the translation manifest. Under the data: array listing, map your external Vault properties directly to the environment variable targets your application expects:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: ai-service-secure-vault
spec:
refreshInterval: "10s" # Quick check window for immediate rotation feedback
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: ai-app-runtime-secrets # The name of the native K8s secret generated by the operator
creationPolicy: Owner
data:
- secretKey: OPENAI_API_KEY # Env var key expected by app.ts
remoteRef:
key: ai-service-credentials # Path inside your Vault
property: api_key # JSON key inside your Vault path
- secretKey: REDIS_PASSWORD
remoteRef:
key: ai-service-credentials
property: redis_pass
🧠 The Bulk Alternative: Handling Heavy Configurations
If your application scales up to require dozens of environment parameters, writing four configuration lines for every single key becomes tedious. Fortunately, the operator supports an enterprise feature called dataFrom.
Instead of mapping variables one by one, you can instruct the engine to automatically extract an entire JSON object block out of Vault and convert all its internal JSON keys into independent environment variables automatically:
spec:
target:
name: ai-app-runtime-secrets
# --- THE BULK REFACTOR ALTERNATIVE ---
dataFrom:
- extract:
key: ai-service-credentials # Grabs ALL keys stored at this Vault path at once
🚀 Verifying the Secret Sync Handshake
Once your configurations are pushed to the cluster, verify the operator status:
kubectl get externalsecret ai-service-secure-vault
- Expected Output: The
STATUScolumn will readSecretSynced.
To verify that the payload crossed the network securely, look at the dynamically generated Kubernetes Secret:
kubectl get secret ai-app-runtime-secrets -o jsonpath="{.data.OPENAI_API_KEY}" | base64 --decode
Conclusion
By leveraging the External Secrets Operator, your cluster infrastructure becomes completely self-healing. You can securely commit structural YAML files to your Git repository, knowing that your live application environments pull sensitive keys dynamically, securely, and seamlessly at runtime.