Francisco Giraldo

Automating Secret Injection: Bridging Vaults to Pods with the External Secrets Operator (ESO)

Published 20 Oct 2026 10 min read

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 STATUS column will read SecretSynced.

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.




Keep reading other of my blog posts!