May 14, 2026
Posted by Jeremy Hess
Kubernetes Secrets solve a real problem, getting credentials into containers, but they introduce a bigger one: those secrets are just base64-encoded strings stored in etcd, with no native encryption at the object level. As organizations mature, a security team inevitably asks: how do we enforce encryption, rotation, and lifecycle policies on everything in the cluster without rewriting every application that already reads from Kubernetes Secrets?
The External Secrets Operator (ESO) is the answer the cloud-native ecosystem converged on. It bridges your existing secrets management platform and your Kubernetes workloads, keeping your apps unchanged while moving the actual secret values into a purpose-built store like Akeyless. This guide covers how ESO works, how to set it up with Akeyless step by step, the security best practices that matter in production, and the answers to the questions DevSecOps teams ask most often.
| Key Takeaways • Kubernetes Secrets are base64-encoded, not encrypted by default. ESO moves the source of truth to a dedicated secrets manager while keeping your applications consuming standard K8s Secrets. • ESO introduces three CRDs: ExternalSecret (what to fetch), SecretStore (how to authenticate, namespace-scoped), and ClusterSecretStore (cluster-wide equivalent). • The reconciliation loop polls at your configured refreshInterval (1 hour is the standard default). Set alerts for SecretSyncedError conditions so failures surface immediately. • Akeyless’s provider for ESO supports API Key, Kubernetes JWT, AWS IAM, Azure AD, and GCP auth methods, choose the one that matches your cluster’s identity model. • Use namespaced SecretStores for multi-tenant clusters and reserve ClusterSecretStore for genuinely shared secrets; ESO has cluster-wide write access, so scope matters. • ESO does not restart pods when a Secret changes. Combine it with Stakater Reloader or equivalent tooling if your application needs to pick up rotated secrets without redeployment. |
What is the External Secrets Operator (ESO)?
The External Secrets Operator is an open-source Kubernetes operator that extends the Kubernetes API with Custom Resources defining where secrets live and how to synchronize them into the cluster. The controller fetches secrets from an external API and creates Kubernetes Secret objects; if the external secret changes, the controller reconciles the state and updates the in-cluster secret accordingly.
ESO supports a wide range of secret backends out of the box, including Akeyless, AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager, IBM Secrets Manager, and dozens more. The provider abstraction means that teams can migrate from one backend to another by updating the SecretStore configuration, not the application code or ExternalSecret definitions.
The project is hosted under the external-secrets GitHub organisation and has accumulated over 4,000 stars and hundreds of contributors, making it one of the most actively maintained Kubernetes ecosystem projects for secrets management.
About the External Secrets Project
ESO evolved from the earlier Kubernetes External Secrets project, which was limited in extensibility and provider support. The current operator was redesigned using the Kubernetes controller-runtime framework, introducing well-defined CRDs, a richer provider API, and full RBAC awareness. This architectural shift enabled the broad provider ecosystem ESO supports today.
The project is governed as a community effort with a formal API compatibility commitment, provider-specific fields follow the same versioning discipline as the core CRDs, so upgrades do not silently break existing SecretStore or ExternalSecret manifests. If you are evaluating ESO for the first time, the official documentation at external-secrets.io is the authoritative reference for supported providers, API versions, and release notes.
Why Use External Secrets in Kubernetes
Organizations that are already using Kubernetes Secrets for credential management need a more secure method for storing and managing the full secret lifecycle, one that enforces encryption, supports rotation, and integrates with audit pipelines. That is why ESO exists. But the operational case is equally important: teams that have already invested in automation, CI/CD pipelines, and Helm charts around Kubernetes Secrets do not want to refactor that infrastructure. ESO lets them strengthen the security model without changing the application contract.
The specific pain points ESO addresses:
- Manual secret sync: Without ESO, rotating a secret in your external store requires a manual kubectl apply or a custom script to update the in-cluster Secret. ESO makes that automatic.
- Configuration drift: Secrets in Kubernetes and secrets in the provider get out of sync when humans are in the loop. The reconciliation loop eliminates drift by continuously polling the source of truth.
- Rotation lag: Unrotated secrets accumulate risk. ESO’s refreshInterval combined with short-lived secrets in Akeyless gives you near-real-time delivery of rotated values.
- Audit gaps: Native Kubernetes Secrets have no access logs. Routing through Akeyless or another provider gives you a proper audit trail of who read which secret and when.
Manual secrets management vs ESO – at a glance:
| Manual Kubernetes Secrets | ESO + External Provider |
|---|---|
| Base64-encoded, no native encryption at rest | Encrypted and managed in a purpose-built secrets store |
| Rotation requires manual kubectl apply or scripts | Automatic reconciliation on every refreshInterval |
| No audit trail at the Kubernetes level | Full audit log in the provider (Akeyless, Vault, AWS, etc.) |
| Secrets often committed to GitOps repos | Only ExternalSecret manifests in Git, no secret values |
| Access control via Kubernetes RBAC only | RBAC in both Kubernetes and the provider; least-privilege by default |
How External Secrets Operator Works
Understanding ESO’s mechanics requires understanding its three core CRDs and the reconciliation loop that ties them together.
The Three Core CRDs
SecretStore defines how to authenticate with an external secret provider. It is namespace-scoped, meaning that an ExternalSecret in one namespace can only reference a SecretStore in the same namespace. This is the right choice for multi-tenant clusters where teams should not share credential paths.
ClusterSecretStore is the cluster-wide equivalent of SecretStore. Any ExternalSecret in any namespace can reference a ClusterSecretStore. Use it for shared secrets that genuinely need to be accessible everywhere, and carefully, since ESO has write access to Kubernetes Secrets cluster-wide.
ExternalSecret defines what to fetch and how to project it into a Kubernetes Secret. It references a SecretStore (or ClusterSecretStore), specifies the remote path of the secret in the external provider, and maps it to one or more keys in the resulting Kubernetes Secret. The ExternalSecret also carries the refreshInterval that governs how often ESO polls for changes.
The Reconciliation Loop
ESO runs a controller that continuously watches ExternalSecret resources. On each reconciliation cycle:
- ESO resolves the referenced SecretStore and instantiates an authenticated API client for the provider.
- It fetches the secret value from the external provider using the credentials in the SecretStore.
- It decodes and optionally transforms the value using any template defined in the ExternalSecret.
- It creates or updates the Kubernetes Secret object specified in target.name.
- If the external secret changes before the next scheduled poll, the change is picked up on the next refreshInterval cycle.
The reconciliation status is exposed as conditions on the ExternalSecret resource itself, kubectl describe externalsecret <name> shows whether the last sync succeeded (SecretSynced) or failed (SecretSyncedError), with a human-readable message indicating the cause.
One important caveat: ESO syncs the Secret object, but Kubernetes does not restart pods when a Secret changes. If your application needs to pick up a rotated secret at runtime, you need a complementary tool such as Stakater Reloader to trigger rolling restarts when the Secret is updated.
Setting Up ESO with Akeyless: Step-by-Step
The following walkthrough covers the minimal working path from a fresh cluster to syncing your first Akeyless secret into Kubernetes. Prerequisites: a running Kubernetes cluster (v1.19+), Helm installed locally, and at least one Akeyless Auth Method configured with an Access Role that grants read permission to the secret you want to sync.
Step 1: Install ESO via Helm
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=true
Verify the installation:
kubectl get pods -n external-secrets
You should see the external-secrets, external-secrets-cert-controller, and external-secrets-webhook pods running.
Step 2: Store Akeyless Credentials in a Kubernetes Secret
ESO authenticates to Akeyless using an Access ID and the selected auth method. Store these in a Kubernetes Secret in the same namespace as your SecretStore:
apiVersion: v1
kind: Secret
metadata:
name: akeyless-secret-creds
namespace: my-app-namespace
type: Opaque
stringData:
accessId: "p-XXXXXX" # Your Akeyless Access ID
accessType: "api_key" # api_key | k8s | aws_iam | azure_ad | gcp
accessTypeParam: "<api_key>" # Only required for api_key and k8s auth methods
Supported Akeyless auth methods for ESO: API Key, Kubernetes JWT (native k8s auth), AWS IAM, Azure AD, and GCP. For production clusters, Kubernetes JWT auth avoids the need for a long-lived API key secret, the ESO pod uses the bound service account token to authenticate with the Akeyless Gateway.
Step 3: Create the SecretStore
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: akeyless-secret-store
namespace: my-app-namespace
spec:
provider:
akeyless:
akeylessGWApiURL: "https://api.akeyless.io"
# For private/self-hosted Gateways, use:
# akeylessGWApiURL: "https://your.akeyless.gw:8080/v2"
authSecretRef:
secretRef:
accessID:
name: akeyless-secret-creds
key: accessId
accessType:
name: akeyless-secret-creds
key: accessType
accessTypeParam:
name: akeyless-secret-creds
key: accessTypeParam
Step 4: Create the ExternalSecret
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: my-app-namespace
spec:
refreshInterval: 1h
secretStoreRef:
name: akeyless-secret-store
kind: SecretStore
target:
name: database-credentials # Name of the K8s Secret to create
creationPolicy: Owner
data:
- secretKey: username # Key in the resulting K8s Secret
remoteRef:
key: /apps/prod/db-username # Full path of the secret in Akeyless
- secretKey: password
remoteRef:
key: /apps/prod/db-password
Apply both manifests and verify the sync:
kubectl apply -f secret-store.yaml
kubectl apply -f external-secret.yaml
kubectl describe externalsecret database-credentials -n my-app-namespace
# Status.Conditions should show: Type=Ready, Status=True, Reason=SecretSynced
Akeyless Provider for External Secrets
The Akeyless provider for ESO is a first-class, maintained provider in the ESO project. Beyond basic static secret retrieval, Akeyless brings capabilities that other providers cannot match through ESO integration alone:
- DFC-encrypted secrets: Secrets stored in Akeyless are protected by Distributed Fragments Cryptography, keys are split across fragments and never assembled in a single location. This means that even the secrets ESO fetches and writes to Kubernetes are sourced from material that was never exposed as a whole in any single system.
- Dynamic secrets: Akeyless can generate short-lived, ephemeral credentials on demand (database credentials, cloud IAM tokens, SSH certificates). ESO can sync these into Kubernetes Secrets at each refresh cycle, giving your pods genuinely time-limited credentials rather than long-lived static values.
- RBAC and access roles: Akeyless Access Roles control which auth identities can read which secret paths. Combined with ESO’s namespace-scoped SecretStore, you can enforce the principle of least privilege at both the Kubernetes layer and the secrets management layer simultaneously.
- PushSecret support: ESO’s PushSecret CRD lets you reverse the flow, pushing a Kubernetes Secret into Akeyless. This is useful when secrets originate in-cluster (for example, auto-generated TLS certificates from cert-manager) and need to be centralised in Akeyless for policy management and cross-cluster distribution.
For ClusterSecretStore targeting Akeyless, use this when you need a shared store accessible from all namespaces:
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: akeyless-cluster-store
spec:
provider:
akeyless:
akeylessGWApiURL: "https://api.akeyless.io"
authSecretRef:
secretRef:
accessID:
name: akeyless-secret-creds
key: accessId
namespace: external-secrets # namespace required for ClusterSecretStore
accessType:
name: akeyless-secret-creds
key: accessType
namespace: external-secrets
accessTypeParam:
name: akeyless-secret-creds
key: accessTypeParam
namespace: external-secrets
See the full Akeyless ESO integration documentation for Kubernetes JWT auth configuration, private Gateway setup, and advanced DataFrom patterns for bulk secret syncing.
External Secrets Operator Best Practices
ESO runs with elevated cluster privileges, it can read and write Kubernetes Secrets across all namespaces. Getting the operational model right from the start is significantly easier than refactoring it after you have dozens of ExternalSecrets deployed.
Namespace Isolation and Scoped Access
Default to namespaced SecretStores. Only promote to ClusterSecretStore when a secret genuinely needs to be shared cluster-wide. ESO’s security documentation is explicit: cluster-wide resources like ClusterSecretStore have access to Secret resources across all namespaces, so exercise caution and minimise RBAC permissions to the necessary minimum. If your cluster hosts multiple teams, each team’s namespace should have its own SecretStore pointing to a scoped set of Akeyless paths, not a shared ClusterSecretStore.
Optimal refreshInterval Settings
- 1 hour is the default and is appropriate for most static secrets. It balances API rate limits against currency.
- 15–30 minutes for secrets that rotate frequently (database credentials, short-lived tokens).
- Use refreshInterval: 0 only for immutable secrets you never want re-synced after initial creation, ESO will not poll again unless you manually annotate the ExternalSecret to force a refresh.
- Be mindful of provider API rate limits: a cluster with 500 ExternalSecrets at a 1-minute refresh interval generates 500 API calls per minute to your secrets backend.
Audit Logging
Enable access logging in Akeyless for every Access Role your ESO SecretStore uses. Route Akeyless audit events to your SIEM. On the Kubernetes side, enable audit logging for secrets resource operations and correlate with ESO pod logs to create an end-to-end trace of every secret read and write. This is a baseline requirement for PCI DSS, SOC 2, and ISO 27001 compliance scopes.
Secret Expiry and Rotation Handling
ESO syncs values but does not restart pods. Add Stakater Reloader or a custom webhook to trigger rolling restarts when targeted Secrets change. For secrets with hard expiry (client certificates, short-lived database credentials), set the refreshInterval to be notably shorter than the TTL, if a credential expires after 2 hours, poll every 30 minutes, not every 1 hour.
DevSecOps Checklist
- ⬜ ESO installed via Helm with installCRDs=true and version pinned in your infrastructure-as-code.
- ⬜ SecretStore scoped to namespace unless a documented exception for ClusterSecretStore.
- ⬜ Akeyless Access Role grants read only to the specific secret paths the namespace requires, not wildcard (/*) paths.
- ⬜ refreshInterval set based on rotation frequency and provider rate limits, documented per ExternalSecret.
- ⬜ Alerts configured for SecretSyncedError conditions routed to on-call, not shared mailbox.
- ⬜ Pod restart strategy defined (Reloader, rolling restart, or application handles live Secret updates).
- ⬜ Akeyless audit logs flowing to SIEM with correlation to Kubernetes audit logs.
- ⬜ PushSecret configured for any in-cluster-generated secrets that need to be centralized in Akeyless.
Frequently Asked Questions
What is the difference between ExternalSecret and SecretStore in ESO?
A SecretStore (or ClusterSecretStore) defines how to connect and authenticate with an external secret provider, it holds the credentials and endpoint configuration for Akeyless, Vault, AWS, or another backend. An ExternalSecret defines what to fetch from that provider, the specific secret paths, the keys to map them to in the resulting Kubernetes Secret, and how often to refresh. Think of SecretStore as the connection config and ExternalSecret as the query. Multiple ExternalSecrets can reference the same SecretStore.
Can ESO sync secrets from multiple providers simultaneously?
Yes. You can have multiple SecretStores in a namespace, one pointing to Akeyless, another to AWS Secrets Manager, a third to a HashiCorp Vault instance, and individual ExternalSecrets can reference whichever store holds the relevant secret. This is common in migration scenarios where teams are consolidating onto a single provider: ESO lets you run both providers in parallel until the migration is complete, with no changes to applications.
How do I rotate secrets automatically using External Secrets Operator?
Rotation in ESO is driven by refreshInterval. When ESO polls Akeyless (or another provider) at the next interval, it picks up the latest version of the secret and updates the Kubernetes Secret object. The rotation of the actual secret value is handled in Akeyless, using automatic rotation policies, dynamic secrets, or triggered rotations via API. For the pods consuming the Secret to pick up the new value, you need either a pod restart (via Stakater Reloader), or an application built to re-read Secrets from the filesystem or API without restarting.
How does Akeyless compare to other ESO backends like Vault or AWS Secrets Manager?
All three are valid ESO backends. The meaningful differences for Kubernetes workloads:
- Akeyless: SaaS, zero-knowledge (DFC encryption), dynamic secrets, no infrastructure to operate. Free tier available. Best for teams that want enterprise capabilities without running a Vault cluster.
- HashiCorp Vault: Self-hosted (or HCP Vault), highly flexible, strong open-source community. Requires infrastructure investment and operational expertise to run reliably at scale.
- AWS Secrets Manager: Native to AWS, tightly integrated with IAM and other AWS services. Optimal for AWS-only workloads; costs increase linearly with secret count and rotation frequency. Not provider-agnostic.
| Secure Kubernetes secrets with AkeylessAkeyless connects to ESO in minutes, brings zero-knowledge encryption to every synced secret, and supports dynamic secrets, RBAC, and full audit trails, without running any infrastructure.Book a custom demo or start free today. |