
August 21, 2026
9 min read
Table of Contents
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.
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.
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.
| Criteria | External Secrets Operator | Vault Agent Injector |
|---|---|---|
| Secret format | Native Kubernetes Secrets | Injected files or env vars via sidecar |
| Application compatibility | Works with any app expecting K8s secrets | Requires annotation-aware admission controller |
| Rotation mechanism | Polling-based, configurable interval | Sidecar re-renders templates on lease renewal |
| GitOps friendliness | Excellent — ExternalSecret CRDs are declarative YAML | Moderate — annotations live on pod specs |
| Operational overhead | Single controller, centralized stores | Sidecar per pod, template management |
| Multi-cluster support | Native via ClusterSecretStore | Requires Vault Agent config per cluster |
| Best for | Standard K8s workloads, GitOps pipelines | Legacy 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.
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.caBundleor mount the CA as a volume. Self-signed certificates without explicit trust anchors cause immediateClusterSecretStorereadiness failures. - Service account permission drift: When renaming namespaces or service accounts during refactoring, the Vault role’s
bound_service_account_namesbecomes stale. ESO logs show 403 errors. Always update Vault roles alongside Kubernetes RBAC changes. - Secret deletion race conditions: Deleting an ExternalSecret with
creationPolicy: Ownerremoves the Kubernetes secret immediately. Pods still running may crash if they haven’t cached the value. Implement graceful shutdown handlers or useRetainpolicy 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 whenversion: v2is set, but manual API calls or debugging sessions often forget it. Always verify paths withvault kv getbefore 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.

