Skip to main content

Install

The installation is quite simple and can be done using Helm for the External Secrets Operator. What changes and brings a bit more complexity is configuring the provider and mapping the settings.

The Operator installs the Custom Resource Definitions that will extend the API and deploy the External Secrets, which is the controller that manages the defined objects.

helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace \

Checking what we have in the cluster.

❯ k get all -n external-secrets
NAME READY STATUS RESTARTS AGE
pod/external-secrets-547987d945-d88xw 1/1 Running 0 6h45m
pod/external-secrets-cert-controller-f79f56cf4-7r2mx 1/1 Running 0 6h45m
pod/external-secrets-webhook-6cb79956f4-6gqn2 1/1 Running 0 6h45m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/external-secrets-webhook ClusterIP 172.20.142.40 <none> 443/TCP 6h45m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/external-secrets 1/1 1 1 6h45m
deployment.apps/external-secrets-cert-controller 1/1 1 1 6h45m
deployment.apps/external-secrets-webhook 1/1 1 1 6h45m

NAME DESIRED CURRENT READY AGE
replicaset.apps/external-secrets-547987d945 1 1 1 6h45m
replicaset.apps/external-secrets-cert-controller-f79f56cf4 1 1 1 6h45m
replicaset.apps/external-secrets-webhook-6cb79956f4 1 1 1 6h45m

We can observe 3 pods.

  • external-secrets: The core of the External Secrets Operator. This is the pod that actually fetches data from Secrets Manager or SSM.
    • Fetches secrets from backends (AWS, GCP, Vault, etc).
    • Reconciles ExternalSecret with K8s Secret.
    • Applies refreshInterval, dataFrom, target.creationPolicy, etc.
    • Watches resources of type ExternalSecret/SecretStore/ClusterSecretStore
  • external-secrets-cert-controller: Manages ESO's internal certificates
    • Creates and renews TLS certificates used internally by ESO webhooks.
    • Ensures that communication between K8s API Server and webhooks is secure (via certificates).
    • Creates a Certificate and Issuer using cert-manager or self-signed.
  • external-secrets-webhook: Validation and security Validates manifests before applying (ValidatingWebhook and MutatingWebhook)
    • Adds default values (like default refreshInterval, creationPolicy, etc).
    • Performs checks such as:
      • "Is the backend type supported?"
      • "Is the resource well-formed?"
    • It acts at the moment you create or modify a resource (like kubectl apply).

If we then run the kubectl apply -f external-secret.yaml command (a manifest of type ExternalSecret for example), the flow will be:

  1. Kubectl sends the request to the Kubernetes API.

  2. After the authentication and authorization that kube-api performs, a third step is the admission controller. The kube-api server checks if there is any validation/mutation webhook registered for the Custom Resource ExternalSecret. The webhook is a service and at this moment the external-secrets-cert-controller is already working behind the scenes, ensuring the webhook has valid and securely rotated certificates.

  3. The external-secrets-webhook takes action

    • The API Server makes an HTTPS call to the external-secrets-webhook pod.
    • The webhook performs:
      • Validation of required fields
      • Checks if the backend type is valid (aws, gcp, vault, etc)
      • Adds default values if you forgot them (e.g.: refreshInterval: 1h)
    • If everything is ok, the webhook returns a 200 OK and the resource is persisted in etcd.
  4. The external-secrets (controller) detects the creation

    • The controller "watches" all ExternalSecret, SecretStore, etc, via watch in K8s.
    • When it detects a new ExternalSecret, it:
      • Resolves the SecretStore/ClusterSecretStore
      • Uses the configured credentials (IAM, accessKey, IRSA, etc)
      • Accesses the backend (e.g.: AWS SSM, Secrets Manager)
      • Fetches the values using:
        • data
        • dataFrom
      • Constructs a K8s Secret with the obtained values

When to scale the external-secrets-controller?

  • Many secrets (~thousands) being reconciled frequently.
  • Very high latency to fetch data from backends.
  • Heavy multi-tenant usage with many ExternalSecrets running per second.

Then you can scale the external-secrets controller with more replicas and use the sharding feature (it's supported).

The external-secrets-cert-controller only runs periodic certificate tasks, it doesn't need scaling. The webhook is quite simple, it only receives calls from the API Server and has low load, so at most 2 pods if needed, but not for load reasons, rather for resilience.

What would a production values.yaml look like.

replicaCount: 2  # High availability for the controller

resources:
limits:
cpu: 250m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi

# Add health check probes
controllerManager:
container:
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10

# Tolerations to allow execution on specific nodes, such as infra nodes
# tolerations:
# - key: "dedicated"
# operator: "Equal"
# value: "infra"
# effect: "NoSchedule"

# If you want to run only on nodes with a specific label
# nodeSelector:
# node-role.kubernetes.io/infra: "true"

# Priority for the controller (avoid preemption)
priorityClassName: "system-cluster-critical"

# Webhook config
webhook:
replicaCount: 2
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi

# Cert controller
certController:
replicaCount: 1
resources:
limits:
cpu: 50m
memory: 64Mi
requests:
cpu: 25m
memory: 32Mi

installCRDs: false

# (Optional) Sharding — divides ExternalSecrets among replicas
sharding:
enabled: true
replicaCount: 2 # Must match controllerManager.replicaCount

# Enabled metrics
metrics:
serviceMonitor:
enabled: true # Requires Prometheus Operator

# Leader election for HA
# It's a mechanism where only one of the pods is the active leader, and the others stay on standby.
# This leader is the only one that executes ExternalSecrets reconciliation (or sharding, if active).
leaderElection:
enabled: true

What is Sharding

It's ESO's ability to automatically divide the ExternalSecrets reconciliation load among multiple controller replicas.

When you have many ExternalSecrets (e.g.: hundreds or thousands), a single ESO replica can:

  • Become overloaded
  • Have delays in refreshInterval
  • Generate throttling in AWS/GCP/Vault

With sharding, you spread the load among the pods, maintaining performance and reliability.

Each ExternalSecret is hashed based on metadata.name + namespace. This hash is used to determine which controller will handle which resource. Thus, each replica processes only a slice of the total universe.

Flow with replicaCount: 3 and sharding.enabled: true Imagine you have 3 ESO pods:

Pod 1 → processes ExternalSecrets whose hash ends in 0

Pod 2 → hash ends in 1

Pod 3 → hash ends in 2

Each pod processes only what's "theirs", and ignores the rest.

SituationSharding
< 100 ExternalSecrets❌ Not needed
100–500, latency ok⚠️ Optional
500+ secrets or slow/throttling backends✅ Recommended
Multi-tenant clusters with many namespaces✅ Yes