Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

External Secrets Operator with Vault

By Kokil Thapa | Last reviewed: August 2026

Managing secrets in Kubernetes often devolves into encrypted YAML files scattered across Git repositories or environment variables injected at deploy time. The External Secrets Operator with Vault solves this by synchronizing secrets from HashiCorp Vault directly into native Kubernetes Secret objects, keeping your cluster state aligned with your central secret store. This approach eliminates manual copying, enables automatic rotation, and enforces access control through Vault policies rather than RBAC alone.

If you are building CI/CD pipelines or managing multi-environment deployments, integrating ESO early prevents secret sprawl before it starts. I have used this pattern on production Laravel applications and legal-tech portals where database credentials, API keys, and payment gateway tokens must rotate without redeploying pods. The setup requires coordination between platform engineers and security teams, but once established, it removes an entire class of deployment failures caused by stale or missing secrets.

How does External Secrets Operator with Vault architecture work?

Understanding the data flow prevents misconfiguration. ESO runs as a controller inside your cluster. It does not push secrets; it pulls them. The operator watches for ExternalSecret custom resources, authenticates against Vault using the configured method, fetches the specified key-value pairs, and creates or updates standard Kubernetes Secrets. Your application pods mount these native secrets exactly as they would any other K8s secret, requiring zero code changes.

HashiCorp VaultSecret Engine (KV v2)• Auth Methods• Policies & Roles• Lease ManagementHTTPS / TLSKubernetes ClusterESO ControllerWatches ExternalSecret CRDsNative K8s SecretSynced & Auto-UpdatedApplication PodPlatform TeamGitOps Repository• ExternalSecret YAML• ClusterSecretStore• No Raw Secrets
External Secrets Operator with Vault architecture: ESO pulls secrets over TLS and creates native Kubernetes secrets for pod consumption

This pull-based model is critical. Vault never needs network access to your cluster; only ESO needs outbound access to Vault. This simplifies firewall rules and aligns with zero-trust networking principles common in Nepal’s financial and legal-tech sectors where inbound connections to infrastructure are heavily restricted.

How do you configure Vault Kubernetes authentication for ESO?

Before ESO can read anything, Vault must trust your cluster. Kubernetes authentication uses the pod’s service account token to verify identity. Never use static AppRole credentials for ESO in a Kubernetes environment; K8s auth binds permissions to specific service accounts in specific namespaces, providing far tighter isolation.

Enable and configure the Kubernetes auth method

Run these commands against your Vault instance. Replace vault.example.com with your actual Vault address and ensure the Kubernetes API server URL matches your cluster’s internal endpoint.

vault auth enable kubernetes

vault write auth/kubernetes/config \
    kubernetes_host="https://kubernetes.default.svc.cluster.local" \
    kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

vault write auth/kubernetes/role/eso-role \
    bound_service_account_names=external-secrets-operator \
    bound_service_account_namespaces=external-secrets \
    policies=eso-read-policy \
    ttl=1h

The bound_service_account_names and bound_service_account_namespaces parameters are non-negotiable security controls. Without them, any pod in any namespace could authenticate as this role. Always restrict to the exact service account ESO uses.

Create a least-privilege Vault policy

Grant only the paths ESO needs. For a typical Laravel application pulling database credentials and mail configuration:

path "secret/data/laravel-app/*" {
  capabilities = ["read"]
}

path "secret/metadata/laravel-app/*" {
  capabilities = ["list", "read"]
}

Save this as eso-read-policy.hcl and apply it with vault policy write eso-read-policy eso-read-policy.hcl. Note the separate metadata path; ESO sometimes lists keys during reconciliation, and omitting metadata access causes silent sync failures.

How do you install and configure External Secrets Operator?

Install ESO using Helm. As of 2026, ESO v0.15.x is stable and supports Vault KV v2 natively. Pin your version in production; do not float on latest.

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

helm install external-secrets external-secrets/external-secrets \
  --namespace external-secrets --create-namespace \
  --set installCRDs=true \
  --version 0.15.2

After installation, create a ClusterSecretStore. This cluster-scoped resource defines how ESO connects to Vault. Namespace-scoped SecretStore works too, but ClusterSecretStore avoids duplication across environments.

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "eso-role"
          serviceAccountRef:
            name: "external-secrets-operator"
            namespace: "external-secrets"

Verify connectivity before proceeding: kubectl get clustersecretstore vault-backend should show READY=True. If it shows False, check ESO logs with kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets-operator. Common issues include incorrect CA certificates, wrong auth mount path, or mismatched service account names.

Choose Vault Auth MethodRunning inside Kubernetes?YesNoKubernetes Auth✓ Recommended for ESO✓ SA-bound, no static creds✓ Auto token rotationAppRole / Token⚠ Static credentials⚠ Manual rotation neededUse only outside K8sCreate ClusterSecretStoreauth.kubernetes.role: eso-roleCreate SecretStore (NS)Manage creds via K8s Secret
Decision tree for selecting Vault authentication when configuring External Secrets Operator with Vault in Kubernetes versus external environments

How do you define ExternalSecret resources for application secrets?

The ExternalSecret resource maps Vault paths to Kubernetes secret keys. Keep one ExternalSecret per logical application or service. Avoid monolithic secrets containing dozens of unrelated keys; granular secrets simplify rotation and reduce blast radius.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: laravel-app-secrets
  namespace: production
spec:
  refreshInterval: 5m
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: laravel-app-env
    creationPolicy: Owner
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: laravel-app/database
        property: password
    - secretKey: MAIL_API_KEY
      remoteRef:
        key: laravel-app/mail
        property: api_key
    - secretKey: PAYMENT_GATEWAY_SECRET
      remoteRef:
        key: laravel-app/payments
        property: esewa_secret_key

The refreshInterval controls how often ESO polls Vault. Five minutes balances freshness against Vault load. For high-security contexts like payment processing on Laravel payment integrations, reduce this to one minute. For static configuration that rarely changes, fifteen minutes is acceptable.

Note the creationPolicy: Owner setting. This ensures ESO owns the Kubernetes secret and deletes it when the ExternalSecret is removed. Use Merge only when combining Vault-sourced keys with manually managed keys in the same secret, which I generally discourage because it obscures the source of truth.

How does External Secrets Operator compare to Vault Agent Injector?

Both tools integrate Vault with Kubernetes, but they serve different operational models. Choosing incorrectly leads to unnecessary complexity or insufficient integration with existing workflows.

CriteriaExternal Secrets OperatorVault Agent Injector
Secret formatNative Kubernetes SecretsInjected files or env vars via sidecar
Application compatibilityWorks with any app expecting K8s secretsRequires annotation-aware admission controller
Rotation mechanismPolling-based, configurable intervalSidecar re-renders templates on lease renewal
GitOps friendlinessExcellent — ExternalSecret CRDs are declarative YAMLModerate — annotations live on pod specs
Operational overheadSingle controller, centralized storesSidecar per pod, template management
Multi-cluster supportNative via ClusterSecretStoreRequires Vault Agent config per cluster
Best forStandard K8s workloads, GitOps pipelinesLegacy apps needing file-based secrets, dynamic DB creds

In practice, I default to ESO for new projects. Native Kubernetes secrets integrate cleanly with Helm charts, Kustomize overlays, and ArgoCD without custom mutation webhooks. Vault Agent Injector remains valuable for dynamic database credentials where short-lived leases matter more than Kubernetes-native representation, or when migrating legacy applications that read secrets from filesystem paths.

External Secrets OperatorVault ServerCentral StoreESO ControllerPolls & SyncsK8s SecretNative ObjectApp PodMounts SecretVault Agent InjectorVault ServerCentral StorePod w/ SidecarVault Agent InitRender TemplatesShared VolumeApp Container⚠ Requires Admission Controller + AnnotationsNot native K8s secrets
External Secrets Operator with Vault creates native K8s secrets while Vault Agent Injector uses sidecars and shared volumes for file-based injection

What production pitfalls occur with External Secrets Operator and Vault?

After deploying this stack across multiple client environments, several failure modes recur. Addressing them proactively saves hours of debugging during incidents.

  • Certificate validation failures: If Vault uses a private CA, ESO will refuse connections unless you provide the CA bundle. Set spec.provider.vault.caBundle or mount the CA as a volume. Self-signed certificates without explicit trust anchors cause immediate ClusterSecretStore readiness failures.
  • Service account permission drift: When renaming namespaces or service accounts during refactoring, the Vault role’s bound_service_account_names becomes stale. ESO logs show 403 errors. Always update Vault roles alongside Kubernetes RBAC changes.
  • Secret deletion race conditions: Deleting an ExternalSecret with creationPolicy: Owner removes the Kubernetes secret immediately. Pods still running may crash if they haven’t cached the value. Implement graceful shutdown handlers or use Retain policy during migrations.
  • Vault token TTL exhaustion: If your Vault role TTL is shorter than ESO’s refresh interval, authentication fails intermittently. Set role TTL to at least 2× the refresh interval. A 1-hour TTL with 5-minute refresh provides ample margin.
  • KV v2 path confusion: Vault KV v2 inserts /data/ between the mount and key path. ESO handles this automatically when version: v2 is set, but manual API calls or debugging sessions often forget it. Always verify paths with vault kv get before writing ExternalSecret specs.

Monitoring matters as much as configuration. Expose ESO metrics via Prometheus and alert on externalsecret_status_condition{condition=Ready,status=False}. Silent sync failures are worse than loud ones; a pod starting with yesterday’s database password creates data corruption risks that surface hours later.

Implementing External Secrets Operator with Vault in production

Start with a single namespace and non-critical secrets to validate the authentication chain before rolling out cluster-wide. Document your Vault policy naming conventions and ExternalSecret templates so team members can self-serve without deep Vault expertise. For teams evaluating whether to bring in specialized help, understanding the operational weight of secret management informs decisions about hiring developers who can own this infrastructure end-to-end.

The combination of External Secrets Operator with Vault delivers genuine secret lifecycle automation when configured correctly. Treat the initial setup as infrastructure code: version your Vault policies, lint your ExternalSecret manifests, and test authentication in staging before touching production. The upfront investment pays dividends every time a credential rotates without a deployment, a pager stays silent, and your audit log shows exactly which service account accessed which secret at what time.

If you need assistance designing or implementing this architecture for your Kubernetes environment, reach out to discuss your secret management requirements.

Frequently Asked Questions

It is a Kubernetes operator that synchronizes secrets from HashiCorp Vault into native Kubernetes Secret objects, allowing applications to consume credentials without direct Vault integration.

ESO uses a ClusterSecretStore or SecretStore resource configured with Kubernetes ServiceAccount authentication, AppRole, or JWT to obtain a Vault token for reading secrets securely.

Yes, ESO is open-source and free; costs relate only to your Vault infrastructure hosting and the compute resources required to run the operator within your Kubernetes cluster.

In my experience managing production clusters, ESO creates native Kubernetes Secrets compatible with all workloads without requiring init containers or sidecars, simplifying deployment manifests and reducing pod startup latency compared to Vault Agent Injector's runtime injection approach.

Set the refreshInterval field in your ExternalSecret manifest to define how often ESO polls Vault for updates. For most production applications I have deployed, a 5-minute interval balances freshness against API load, though sensitive credentials may require 1-minute intervals alongside application-level reload mechanisms.

Yes, you can create separate SecretStore resources pointing to different Vault namespaces or mount paths. Each ExternalSecret references its specific store, enabling multi-tenant architectures where teams access isolated secret backends without cross-namespace leakage or complex policy configurations.

ESO retains the last successfully synced Kubernetes Secret and marks the ExternalSecret condition as degraded. Applications continue functioning with cached credentials until connectivity restores. I always configure alerting on degraded conditions because silent failures during extended outages cause stale credential issues.

Check the ExternalSecret status with kubectl describe externalsecret, then inspect operator logs for authentication or permission errors. Common issues I encounter include expired ServiceAccount tokens, incorrect Vault policy paths, missing read capabilities on the secret engine, or network policies blocking egress to the Vault endpoint.

Yes, ESO handles dynamic secrets by requesting new credentials at each refresh interval. However, applications must handle connection recycling since credentials expire. On client projects using PostgreSQL with Vault's database secrets engine, I pair ESO with connection poolers that gracefully rotate connections when underlying credentials change.

ESO supports KV v1/v2, database, AWS, GCP, Azure, PKI, and transit engines. The provider configuration differs per engine type. For legal-tech portals storing document encryption keys via Transit or managing third-party API tokens through KV v2, I have found ESO handles both patterns reliably.

Apply least-privilege policies granting read-only access to specific paths matching your ExternalSecret definitions. Avoid wildcard permissions. In production deployments, I namespace policies by team or environment so compromised ServiceAccounts cannot access unrelated secrets, and audit logging captures every read operation.

No, ESO is unidirectional from external providers into Kubernetes. If you need bidirectional sync or secret generation workflows, consider combining ESO with Vault's Kubernetes secrets engine or dedicated tools like Bank-Vaults. Most architectures I build treat Vault as the authoritative source and Kubernetes as a consumer cache.

Start with 100m CPU and 128Mi memory requests, scaling based on ExternalSecret count and refresh frequency. On clusters managing hundreds of secrets with frequent rotations, I have observed the controller requiring up to 500m CPU and 512Mi memory during reconciliation bursts, especially after deployments trigger mass resyncs.

Use the templateFrom or data.template fields to reshape Vault JSON structures into application-specific formats. This avoids modifying application code when Vault returns nested objects. I frequently transform Vault KV responses into dotenv-compatible strings or restructure certificate chains for nginx ingress controllers expecting specific key names.

Primary risks include overly broad Vault policies, unencrypted etcd storage exposing synced Kubernetes Secrets, and insufficient RBAC on ExternalSecret resources. Always enable etcd encryption at rest, restrict SecretStore creation to platform teams, enforce Vault audit logging, and regularly review which ServiceAccounts hold read access to sensitive paths.

Share this article

Quick Contact Options
Choose how you want to connect me: