Architecture¶
Related Notes
secrets/external-secrets-operator/index | secrets/external-secrets-operator/architecture | secrets/external-secrets-operator/operations | secrets/external-secrets-operator/security
Overview¶
External Secrets Operator (ESO) is a Kubernetes operator that synchronizes secrets from external secret management systems into native Kubernetes Secret objects. It extends Kubernetes with Custom Resource Definitions (CRDs) that declare where secrets live and how to synchronize them. The controller fetches secrets from an external API and creates or updates Kubernetes Secrets accordingly.
Component Architecture¶
graph TD
subgraph "Kubernetes Cluster"
MGR["ESO Controller Manager"]
ESC["ExternalSecret Controller"]
SSC["SecretStore Controller"]
WH["Admission Webhooks"]
ES["ExternalSecret CRD"]
CSS["ClusterSecretStore CRD"]
SS["SecretStore CRD"]
PS["PushSecret CRD"]
CES["ClusterExternalSecret CRD"]
K8S["Kubernetes Secret"]
MGR --> ESC
MGR --> SSC
MGR --> WH
ESC -->|watches| ES
ESC -->|reads| SS
ESC -->|reads| CSS
ESC -->|creates/updates| K8S
ESC -->|watches| PS
SSC -->|validates| SS
SSC -->|validates| CSS
CES -->|distributes to namespaces| ES
end
subgraph "Provider Plugins"
AWS["AWS Secrets Manager / SSM"]
VLT["HashiCorp Vault"]
GCP["GCP Secret Manager"]
AZ["Azure Key Vault"]
OP["1Password Connect"]
DP["Doppler"]
GL["GitLab"]
WHK["Webhook (custom HTTP)"]
end
ESC -->|fetches secrets| AWS
ESC -->|fetches secrets| VLT
ESC -->|fetches secrets| GCP
ESC -->|fetches secrets| AZ
ESC -->|fetches secrets| OP
ESC -->|fetches secrets| DP
ESC -->|fetches secrets| GL
ESC -->|fetches secrets| WHK
PS -->|pushes secrets| AWS
PS -->|pushes secrets| VLT
Core CRDs¶
SecretStore¶
A namespaced resource that defines how to access an external secret provider. It separates the concern of authentication from the secret data itself. A SecretStore contains:
- Provider configuration -- specifies the external API (AWS, Vault, GCP, Azure, etc.) and its connection parameters.
- Authentication credentials -- references to Kubernetes Secrets holding provider credentials (API keys, tokens, service account references).
- Retry settings -- optional
maxRetriesandretryIntervalfor HTTP connection resilience. - Controller field -- optional selector to target a specific ESO controller instance when running multiple controllers.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: vault-store
namespace: my-app
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "my-app-role"
serviceAccountRef:
name: "my-app-sa"
ClusterSecretStore¶
A cluster-scoped SecretStore that serves as a centralized gateway to a secret provider. Any ExternalSecret in any namespace can reference it. Cluster operators typically manage ClusterSecretStores to provide shared access to organizational secret backends.
The conditions field on ClusterSecretStore can restrict which namespaces are allowed to reference it, enabling multi-tenancy isolation.
ExternalSecret¶
A namespaced resource that declares what data to fetch and how to materialize it as a Kubernetes Secret. Key fields:
| Field | Purpose |
|---|---|
spec.secretStoreRef |
References a SecretStore or ClusterSecretStore |
spec.refreshInterval |
How often to re-sync (default 1 hour) |
spec.target |
Target Secret name, creation policy, and optional template |
spec.data[] |
Explicit key-to-remoteRef mappings |
spec.dataFrom[] |
Bulk extraction of all keys from a remote secret |
spec.target.template |
Go template for transforming secret data before creation |
Creation policies control ownership: - Owner -- ESO creates and manages the Secret lifecycle. Deleting the ExternalSecret deletes the Secret. - Orphan -- ESO creates the Secret but does not delete it when the ExternalSecret is removed. - Merge -- ESO merges values into an existing Secret without overwriting unrelated keys. - None -- ESO does not create the Secret (used with PushSecret or generator patterns).
PushSecret¶
Reverses the normal flow: pushes data from a Kubernetes Secret out to an external provider. Use cases include:
- Syncing certificates generated by cert-manager into Vault or AWS Secrets Manager.
- Distributing auto-generated credentials to external systems.
- Propagating Secrets across clusters.
PushSecret supports deletionPolicy (delete from provider on removal) and updatePolicy to control how provider-side secrets are managed.
ClusterExternalSecret¶
Distributes ExternalSecret resources into selected namespaces across the cluster. The controller creates a copy of the ExternalSecret in each matching namespace, enabling bulk distribution of secrets with a single resource.
Secret Sync Flow¶
sequenceDiagram
participant User as Developer
participant K8s as Kubernetes API
participant Ctrl as ESO Controller
participant Store as SecretStore
participant Prov as External Provider
User->>K8s: Apply ExternalSecret
K8s->>Ctrl: Watch triggers reconciliation
Ctrl->>K8s: Read referenced SecretStore
K8s-->>Ctrl: Return SecretStore config + credentials
Ctrl->>Ctrl: Instantiate provider client from plugin
Ctrl->>Prov: Fetch secret data (GetSecret)
Prov-->>Ctrl: Return secret values
Ctrl->>Ctrl: Apply template engine (Go templating)
Ctrl->>K8s: Create or update Kubernetes Secret
Note over Ctrl: Repeats at spec.refreshInterval
Reconciliation Loop¶
The controller follows a structured reconciliation process:
- Validate SecretStore reference -- ESO checks
spec.secretStoreRefand verifies the SecretStore exists and itsspec.controllerfield matches (for multi-controller setups). - Instantiate provider client -- Using the SecretStore credentials, ESO creates a provider-specific API client from the registered plugin.
- Fetch secret data -- The controller calls the external API to retrieve the requested secret values, decoding them if required.
- Apply template engine -- If
spec.target.templateis defined, the controller processes Go templates against the fetched data. Templates support advanced transformations includingbase64encode,base64decode,fromJson,toString, and conditional logic. - Create or update Secret -- The controller materializes the final Kubernetes Secret, respecting the configured creation policy.
- Periodic re-sync -- The controller re-queues reconciliation at
spec.refreshIntervalto detect and apply upstream changes.
Provider Plugin Architecture¶
ESO uses a Go interface-based plugin system. Each provider implements a common interface that abstracts the external API:
Provider interface:
- GetSecret(ctx, remoteRef) -> []byte
- GetSecretMap(ctx, remoteRef) -> map[string][]byte
- PushSecret(ctx, secret, remoteRef) -> error
- DeleteSecret(ctx, remoteRef) -> error
- Validate() -> error
Supported providers (as of 2026):
| Category | Providers |
|---|---|
| AWS | Secrets Manager, SSM Parameter Store |
| HashiCorp | Vault (KV v1/v2) |
| Google Cloud | Secret Manager |
| Microsoft | Azure Key Vault |
| 1Password | 1Password Connect |
| Others | Doppler, GitLab, Pulumi Cloud, Akeyless, CyberArk, Fortanix, Senhasegura, Yandex Lockbox, Oracle Vault, IBM Cloud Secrets Manager, Alibaba Cloud |
| Custom | Webhook (arbitrary HTTP endpoint) |
Template Engine¶
The template engine processes Go templates defined in spec.target.template. This allows transformation of external secret data before it is written to the Kubernetes Secret.
Supported capabilities:
- Variable substitution -- {{ .secretKey }} references fetched values.
- Built-in functions -- base64encode, base64decode, fromJson, toJson, toString, toUint, toInt, toFloat, toUpperCase, toLowerCase, trim, regexMatch, regexReplaceAll.
- Pipelines and conditionals -- Full Go template control structures (if, range, with).
- Data from extract -- dataFrom.extract automatically maps all keys from the remote secret into the template context.
Multi-Controller Deployment¶
ESO supports running multiple controller instances within a single cluster. Each controller can be scoped to process only SecretStore and ExternalSecret resources with a matching spec.controller field. This enables:
- Separation of tenant workloads.
- Different provider configurations per controller.
- Gradual rollout of controller upgrades.
Multi-Controller Maturity
Running multiple controllers is not widely tested in production. Test thoroughly before relying on this pattern for critical workloads.
Roles and Responsibilities¶
| Role | Responsibility |
|---|---|
| Cluster Operator | Installs ESO, manages ClusterSecretStores, defines access policies |
| Application Developer | Defines ExternalSecrets and application configuration |
Each role maps roughly to a Kubernetes RBAC role. ESO itself runs with elevated privileges to create and manage Secrets across all namespaces.
Deployment Model¶
ESO is typically deployed via Helm into a dedicated namespace (external-secrets-system by default):
Namespace: external-secrets-system
Deployment: external-secrets (single replica by default)
- Controller manager with leader election for HA
- Validating and mutating admission webhooks
ServiceAccount: external-secrets-sa
CRDs: SecretStore, ClusterSecretStore, ExternalSecret, ClusterExternalSecret, PushSecret
For high availability, increase the replica count. The controller-runtime leader election mechanism ensures only one replica actively reconciles at a time.
How It Works¶
Controller reconciliation loop, secret store authentication, provider gRPC interface, and sync lifecycle.
Reconciliation Flow¶
sequenceDiagram
participant K8sAPI as K8s API
participant ESO_H as ESO Controller
participant Store as SecretStore config
participant Provider as External Provider (Vault/AWS/GCP)
participant Secret_H as K8s Secret
ESO_H->>K8sAPI: Watch ExternalSecret resources
K8sAPI-->>ESO_H: ExternalSecret created/updated
ESO_H->>Store: Read SecretStore connection config
ESO_H->>Provider: Authenticate (SA/IAM/AppRole)
ESO_H->>Provider: Fetch secret values
Provider-->>ESO_H: Secret data
ESO_H->>ESO_H: Apply template (if defined)
ESO_H->>Secret_H: Create/Update K8s Secret
Note over ESO_H: Re-sync every refreshInterval (e.g., 1h)
SecretStore Authentication Chain¶
Each SecretStore defines a provider and an auth method. The controller resolves credentials before calling the provider:
| Provider | Auth Method | Mechanism |
|---|---|---|
| AWS Secrets Manager | JWT (IRSA) | ESO assumes the pod's IAM role via sts:AssumeRoleWithWebIdentity |
| AWS Secrets Manager | AccessKey | Static AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY |
| HashiCorp Vault | Kubernetes | ServiceAccount JWT token sent to Vault's auth/kubernetes/login |
| HashiCorp Vault | AppRole | RoleID + SecretID sent to auth/approle/login |
| GCP Secret Manager | Workload Identity | GCP IAM via federated identity |
| Azure Key Vault | Managed Identity | AAD pod identity or user-assigned managed identity |
For ClusterSecretStore, the auth credentials are typically namespace-scoped using auth.targetNamespace references, enabling multi-tenant access from a single store definition.
Provider Interface (Internal)¶
ESO providers implement a Go interface that abstracts secret operations:
GetSecret(ctx, remoteRef) → (secretValue, error)
GetSecretMap(ctx, remoteRef) → (map[string][]byte, error)
SetSecret(ctx, remoteRef, value) → error
Each provider (Vault, AWS SM, GCP SM, etc.) implements this interface. The controller calls the appropriate provider based on the SecretStore's provider field. This architecture allows new providers to be added without modifying the core reconciliation loop.
Sync Lifecycle¶
stateDiagram-v2
[*] --> Pending: ExternalSecret created
Pending --> SecretSynced: First successful sync
SecretSynced --> SecretSynced: refreshInterval tick
SecretSynced --> Error: Provider unreachable / auth failure
Error --> SecretSynced: Retry succeeds
Error --> Error: Retry with backoff
SecretSynced --> Deleted: ExternalSecret deleted
Deleted --> [*]: Owner policy determines Secret cleanup
Ownership Policies¶
The target.creationPolicy controls what happens to the underlying K8s Secret:
| Policy | Behavior |
|---|---|
Owner (default) |
ESO creates and owns the Secret. Deleting the ExternalSecret deletes the Secret. |
Orphan |
ESO creates the Secret but does not set ownerReference. Secret persists after ExternalSecret deletion. |
Merge |
ESO merges values into an existing Secret it does not own. Only modifies specified keys. |
None |
ESO does not create or modify Secrets. Used with template to generate data for other controllers. |
Template Engine¶
ESO can transform secret data before writing to the K8s Secret using Go templates:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
spec:
target:
template:
type: kubernetes.io/tls
data:
tls.crt: "{{ .tlsCert }}"
tls.key: "{{ .tlsKey }}"
data:
- secretKey: tlsCert
remoteRef:
key: prod/cert
property: certificate
- secretKey: tlsKey
remoteRef:
key: prod/cert
property: private_key
Template functions include: base64 encode/decode, replace, toYAML, toJson, and access to all fetched secret values.
PushSecret Flow (Reverse Sync)¶
flowchart LR
K8sS["K8s Secret"] -->|"PushSecret CRD"| ESO_PS["ESO Controller"]
ESO_PS -->|"push"| Vault_PS["Vault / AWS SM / GCP SM"]
style ESO_PS fill:#1565c0,color:#fff
PushSecret enables syncing K8s Secrets to external providers -- the reverse of the normal ExternalSecret flow. Use cases include: - Writing TLS certificates generated by cert-manager to Vault - Pushing service mesh identity credentials to an external store - Synchronizing secrets across clusters via a shared provider
Sources¶
Benchmarks¶
Scope
Performance characteristics, scaling limits, and resource consumption for External Secrets Operator.
Sync Performance¶
| Provider | Secret Fetch Time | Refresh Interval | Notes |
|---|---|---|---|
| AWS Secrets Manager | 50-200ms | 1h default | API rate limits apply |
| HashiCorp Vault | 10-50ms | 1h default | Depends on network |
| Azure Key Vault | 50-200ms | 1h default | Regional latency |
| GCP Secret Manager | 30-100ms | 1h default | Fast in-region |
Resource Consumption¶
| ExternalSecrets Count | Controller CPU | Controller Memory |
|---|---|---|
| 50 | 50-100m | 128Mi |
| 200 | 100-300m | 256Mi |
| 1,000 | 500m-1 | 512Mi-1Gi |
Scaling Limits¶
| Dimension | Limit | Notes |
|---|---|---|
| ExternalSecrets per cluster | 10,000+ | Controller memory is bottleneck |
| Providers per cluster | 50+ | Multiple SecretStores |
| Refresh rate | Provider API limits | AWS: 10,000/sec, Vault: unlimited |
Sourcing Status¶
Unsourced Performance Data
The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.
Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.