How does the External Secrets Operator turn a value in an external secret manager into a Kubernetes Secret, and what does an ExternalSecret's refreshInterval control?
answer
- store says how, ExternalSecret says what
- namespaced vs cluster-scoped store
- target Secret owned by default
- one hour unless you say otherwise
- staleness bound and provider rate
basics
~20 sA SecretStore or ClusterSecretStore defines provider access. An ExternalSecret names remote keys and a target Secret. The operator fetches the values, writes the Secret, and re-reads the provider every refreshInterval, which defaults to one hour.
solid answer
~40 sThe External Secrets Operator splits the job into two custom resources in `external-secrets.io/v1`. A **`SecretStore`** (namespaced) or **`ClusterSecretStore`** (cluster-scoped) holds the provider connection and the credential used to authenticate. An **`ExternalSecret`** references a store through `secretStoreRef`, lists remote keys under `data` (`secretKey` plus `remoteRef.key` and `property`) or `dataFrom`, and names a `target` Secret. The controller reads the provider, writes an ordinary `Secret`, and by default owns it (`creationPolicy: Owner`). `refreshInterval` (default `1h0m0s`) is how long it waits before reading the provider again. `0s` means fetch once. A change to the ExternalSecret itself triggers an immediate re-read. So `refreshInterval` bounds how stale the Secret can be after a rotation, and it also sets your request rate against the provider.
code
yaml · 18 linesapiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: tracking-db
namespace: shipment-tracking
spec:
refreshInterval: 15m
secretStoreRef:
kind: SecretStore
name: tracking-vault
target:
name: tracking-db
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/tracking/db
property: passwordgo deeper
Recall the pieces: a store that knows how to reach the secret manager, an ExternalSecret that names what to fetch, and an ordinary Secret as the result.
Explain the reconcile loop, the one-hour default, what 0s means, and how creationPolicy Owner ties the Secret's lifetime to the ExternalSecret.
Show operational judgment: read the Ready condition, force a sync by changing the ExternalSecret, and know that an outage keeps the last good value while blocking rotation.
Discuss the interval as a trade between staleness after rotation and provider request volume and cost, and how store scoping shapes who can read what.
## Why a bridge is needed Most organisations keep real credentials in a **secret manager**: a Vault-style server or a cloud provider's secret service. Workloads in Kubernetes, however, consume **`Secret`** objects through environment variables or volumes. The **External Secrets Operator (ESO)** is a controller that bridges the two. It reads values from the external system and writes them into native Secrets, so applications need no provider SDK and no provider credentials of their own. ## The two resources ESO's API group is `external-secrets.io`, with `v1` as the stored version. - **`SecretStore`** is namespaced. It describes *how to reach* one provider (`spec.provider`) and *how to authenticate* (for example a ServiceAccount token exchange or a reference to a credential Secret). Only ExternalSecrets in the **same namespace** can use it. - **`ClusterSecretStore`** has the same shape but is cluster-scoped, so any namespace can reference it. Its `spec.conditions` (`namespaceSelector`, `namespaces`, `namespaceRegexes`) can limit which namespaces may use it. - **`ExternalSecret`** is namespaced. It says *what to fetch* and *where to put it*: - `spec.secretStoreRef` holds `name` and `kind` (`SecretStore` or `ClusterSecretStore`). - `spec.data[]` maps one Secret key (`secretKey`) to a remote value (`remoteRef.key`, optionally `remoteRef.property` for one field of a JSON value). - `spec.dataFrom[]` pulls many keys at once, for example by extracting a whole JSON document. - `spec.target` holds the Secret `name`, a `creationPolicy`, a `deletionPolicy` and an optional `template` for shaping the output. ## The reconcile loop 1. The controller watches ExternalSecrets and the Secrets it manages. 2. On reconcile it resolves the store, authenticates to the provider and fetches every referenced value. 3. It builds the target `Secret` and adds the label `reconcile.external-secrets.io/managed: "true"` and an annotation `reconcile.external-secrets.io/data-hash` holding a hash of the data. 4. It creates or updates the Secret and sets the ExternalSecret's `Ready` condition (reason `SecretSynced`, or `SecretSyncedError` on failure). 5. It requeues for the next refresh. The controller skips a refresh only when the interval has not elapsed, the ExternalSecret is unchanged, **and** the target Secret still exists with the right label and data hash. A deleted or hand-edited Secret is therefore treated as drift and rewritten from the provider. ## refreshInterval and refreshPolicy | Setting | Behaviour | |---|---| | `refreshInterval` unset | Defaults to `1h0m0s` | | `refreshInterval: 15m` | Re-reads the provider every 15 minutes | | `refreshInterval: 0s` | Fetches once, then stops periodic refresh | | `refreshPolicy: Periodic` | Refresh on the interval (the effective default) | | `refreshPolicy: OnChange` | Refresh only when the ExternalSecret's spec or metadata changes | | `refreshPolicy: CreatedOnce` | Create the Secret once and never update it | Two consequences matter in interviews: - **Staleness bound.** After a value is rotated in the provider, the Secret can lag by up to one `refreshInterval`. Editing the ExternalSecret, for example by bumping an annotation, forces an early re-read. - **Provider load.** Every refresh is a provider API call. A short interval on hundreds of objects can hit provider rate limits or run up per-request costs. ## Ownership and deletion - `creationPolicy` defaults to **`Owner`**: the Secret gets an `ownerReference` to the ExternalSecret and is garbage-collected when the ExternalSecret is deleted. `Orphan` leaves it behind, `Merge` only merges keys into an existing Secret, `CreateOrMerge` creates the Secret if it is missing and merges into it otherwise, and `None` writes nothing. - `deletionPolicy` defaults to **`Retain`**: if the provider value disappears, the last synced Secret is kept. `Delete` removes it, and `Merge` removes only the affected keys. - If the provider is **unreachable**, the controller marks the ExternalSecret failed and leaves the existing Secret alone, so running workloads keep working with the last good value. ## Worked example The shipment-tracking API needs its database password. A namespaced SecretStore in `shipment-tracking` authenticates with that namespace's ServiceAccount. An ExternalSecret maps `remoteRef.key: prod/tracking/db` and `property: password` to the Secret key `DB_PASSWORD` in a Secret named `tracking-db`, with `refreshInterval: 15m`. Git contains only these two manifests and no secret material. What ESO does **not** do is make the running process use the new value. It updates the Secret object. How Pods pick that up depends on how they consume it.
- The provider value was rotated five minutes ago and you cannot wait for the refresh. How do you force a sync?Change the ExternalSecret object, for example by adding or bumping an annotation with `kubectl annotate --overwrite`. The controller refreshes whenever the ExternalSecret changes, regardless of the interval. Then confirm that the `Ready` condition reports `SecretSynced`.
- Someone runs kubectl edit on the synced Secret and changes the password by hand. What happens?The operator keeps a data-hash annotation and the managed label on the Secret and watches it. The edited data no longer matches the hash, so the next reconcile treats it as drift and rewrites the value from the provider. Manual edits do not stick.
- The provider is down for an hour. Do Pods lose their credentials?No. A failed fetch marks the ExternalSecret `Ready=False` with reason `SecretSyncedError` and leaves the existing Secret untouched, so running and newly started Pods still get the last good value. Alert on the condition, because a rotation during the outage will not propagate.
saying these in an interview costs you the question
- The operator injects values straight into Pods, bypassing Secrets
- refreshInterval restarts the Pods that use the Secret
- A ClusterSecretStore is required for any cross-team setup
- Deleting the ExternalSecret always leaves the Secret behind
- A provider outage wipes the synced Secret
- Shorter refreshInterval is free, so use seconds everywhere