External Secrets Operator adds a useful boundary: applications can consume Kubernetes Secret objects while the source value lives in a dedicated secret manager.
It also adds a new failure path. A backend token, SecretStore, controller, target Secret, and consuming workload all have to agree before rotation is actually safe.
Ownership Model
Write down the owner for each layer:
backend secret manager -> source value and access policy
SecretStore -> authentication and provider configuration
ExternalSecret -> mapping from backend key to Kubernetes Secret
target Secret -> Kubernetes object consumed by workloads
application workload -> reload behavior and verification
platform team -> controller health, RBAC, namespaces, and alerts
If an ExternalSecret fails, do not assume it is an application bug or a secret-manager outage. Walk the chain.
Baseline Checks
Start read-only:
kubectl get clustersecretstore,secretstore -A
kubectl get externalsecret -A
kubectl get secret -A | grep -i '<target-name>'
kubectl -n external-secrets logs deploy/external-secrets --since=2h
Then inspect the specific object:
kubectl -n app-ns describe externalsecret app-secret
kubectl -n app-ns get externalsecret app-secret -o yaml
Look for:
Readycondition.- last refresh time.
- refresh interval.
- SecretStore reference.
- target Secret name.
- backend key or property mapping.
- error messages from the provider.
Rotation Flow
A safe rotation usually looks like this:
write replacement in backend -> ExternalSecret refreshes target Secret
-> consumer reloads or rolls -> application verification passes
-> old credential is revoked upstream
If the backend supports versioned secrets, decide whether the ExternalSecret follows the latest version or a pinned version. Both models can work, but the rollback behavior is different.
Consumer Impact
Before treating an ExternalSecret error as urgent, identify consumers:
kubectl -n app-ns get deploy,statefulset,daemonset -o yaml | grep -n 'secretName\|secretKeyRef'
kubectl -n app-ns get pods -o wide
Classify impact:
- no consumer exists yet.
- consumer exists but target Secret is current.
- target Secret is stale but pods still run.
- target Secret is missing and pods cannot start.
- old credential was revoked and running pods are failing.
This separates a controller configuration issue from active application impact.
GitOps Boundary
Keep the ExternalSecret and SecretStore configuration in Git when the cluster is GitOps-managed. Do not commit the generated target Secret value.
Review:
- whether ArgoCD or Flux owns the
ExternalSecret. - whether the generated target Secret is ignored, adopted, or pruned.
- whether refresh interval changes are reviewed.
- whether backend key names are environment-specific.
- whether namespace owners can create stores or only references.
The generated Secret is runtime state. The mapping and access boundary are declared state.
Failure Model
The common failure is invisible staleness:
backend credential rotates -> ExternalSecret cannot authenticate
-> target Secret remains old -> pods keep running
-> old credential is revoked -> next restart fails
The operating rule: External Secrets Operator is healthy only when controller status, target Secret freshness, backend access, and consumer reload behavior are all visible.