ExternalSecret
The ExternalSecret describes what data should be fetched, how the data should
be transformed and saved as a Kind=Secret:
- tells the operator what secrets should be synced by using
spec.datato explicitly sync individual keys or usespec.dataFromto get all values from the external API. - you can specify how the secret should look like by specifying a
spec.target.template
Template
When the controller reconciles the ExternalSecret it will use the spec.template as a blueprint to construct a new Kind=Secret. You can use golang templates to define the blueprint and use template functions to transform secret values. You can also pull in ConfigMaps that contain golang-template data using templateFrom. See advanced templating for details.
Update behavior with 3 different refresh policies
You can control how and when the ExternalSecret is refreshed by setting the spec.refreshPolicy field. If not specified, the default behavior is Periodic.
CreatedOnce
With refreshPolicy: CreatedOnce, the controller will:
- Create the
Kind=Secretonly if it does not exist yet - Never update the
Kind=Secretafterwards if the source data changes - Update/ Recreate the
Kind=Secretif it gets changed/Deleted - Useful for immutable credentials or when you want to manage updates manually
Example:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: example
spec:
refreshPolicy: CreatedOnce
# other fields...
Periodic
With refreshPolicy: Periodic (the default behavior), the controller will:
- Create the
Kind=Secretif it doesn't exist - Update the
Kind=Secretregularly based on thespec.refreshIntervalduration - When
spec.refreshIntervalis set to zero, it will only create the secret once and not update it afterward - When
spec.refreshIntervalis set to a value greater than zero, the controller will update theKind=Secretat the specified interval or when theExternalSecretspecification changes
Example:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: example
spec:
refreshPolicy: Periodic
refreshInterval: 1h0m0s # Update every hour
# other fields...
OnChange
With refreshPolicy: OnChange, the controller will:
- Create the
Kind=Secretif it doesn't exist - Update the
Kind=Secretonly when theExternalSecret's metadata or specification changes - This policy is independent of the
refreshIntervalvalue - Useful when you want to manually control when the secret is updated, by modifying the
ExternalSecretresource
Example:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: example
spec:
refreshPolicy: OnChange
# other fields...
Manual Refresh
If supported by the configured refreshPolicy, you can manually trigger a refresh of the Kind=Secret by updating the annotations of the ExternalSecret:
kubectl annotate es my-es force-sync=$(date +%s) --overwrite
SyncWindows
syncWindows restricts when periodic refreshes may occur. It is evaluated in UTC and applies only to the Periodic refresh policy (or when refreshPolicy is unset). OnChange and CreatedOnce policies are unaffected.
A sync-windows block carries a shared kind and a list of schedule + duration entries:
kind: allow-- periodic syncs are permitted only while at least one window is active; all other times are blocked.kind: deny-- periodic syncs are blocked while any window is active; all other times proceed normally.
Each entry in windows uses a standard 5-field cron schedule (UTC) and a duration string (e.g. 8h, 30m). The window stays open for duration after each schedule firing. A window entry with an unparseable schedule is silently ignored and treated as inactive, so a typo does not permanently block syncs.
Example: allow syncs only during business hours (Mon-Fri 09:00-17:00 UTC)
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: example
spec:
refreshInterval: 1h
syncWindows:
kind: allow
windows:
- schedule: "0 9 * * 1-5" # weekdays at 09:00 UTC
duration: 8h # window open until 17:00 UTC
Example: block syncs during a Saturday maintenance window (02:00-04:00 UTC)
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: example
spec:
refreshInterval: 30m
syncWindows:
kind: deny
windows:
- schedule: "0 2 * * 6" # Saturdays at 02:00 UTC
duration: 2h # block until 04:00 UTC
Multiple windows
You can list several entries under windows. For kind: allow, the sync is permitted when any window is active. For kind: deny, the sync is blocked when any window is active.
Interaction with refreshInterval
syncWindows only suppresses sync operations -- it does not change how often the controller checks. The controller still requeues at refreshInterval regardless of whether a sync was blocked. This means that if refreshInterval is longer than window.duration, a window could open and close entirely between two consecutive checks and the sync would be missed for that occurrence. This is by design: refreshInterval is the primary driver; syncWindows is a gate on top of it. To ensure no window occurrence is missed, set refreshInterval to a value shorter than the smallest window.duration.
Features
Individual features are described in the Guides section:
- Find many secrets / Extract from structured data
- Templating
- Using Generators
- Secret Ownership and Deletion
- Key Rewriting
- Decoding Strategy
Example
Take a look at an annotated example to understand the design behind the
ExternalSecret.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: "hello-world"
# labels and annotations are copied over to the
# secret that will be created
labels:
acme.org/owned-by: "q-team"
annotations:
acme.org/sha: 1234
spec:
# Optional, SecretStoreRef defines the default SecretStore to use when fetching the secret data.
secretStoreRef:
name: aws-store
kind: SecretStore # or ClusterSecretStore
# RefreshPolicy determines how the ExternalSecret should be refreshed.
# - CreatedOnce: Creates the Secret only if it does not exist and does not update it afterward
# - Periodic: (default) Synchronizes the Secret at intervals specified by refreshInterval
# - OnChange: Only synchronizes when the ExternalSecret's metadata or specification changes
refreshPolicy: Periodic
# RefreshInterval is the amount of time before the values reading again from the SecretStore provider
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" (from time.ParseDuration)
# May be set to zero to fetch and create it once
refreshInterval: "1h0m0s"
# SyncWindows optionally restricts when periodic refreshes may occur (UTC, Periodic policy only).
# kind: allow -- syncs are permitted only while at least one window is active.
# kind: deny -- syncs are blocked while any window is active.
# Each window entry uses a standard 5-field cron schedule and a duration.
syncWindows:
kind: allow
windows:
- schedule: "0 9 * * 1-5" # weekdays at 09:00 UTC
duration: 8h # window open until 17:00 UTC
# the target describes the secret that shall be created
# there can only be one target per ExternalSecret
target:
# The secret name of the resource
# Defaults to .metadata.name of the ExternalSecret
# It is immutable
name: application-config
# Specifies the ExternalSecret ownership details in the created Secret. Options:
# - Owner: (default) Creates the Secret and sets .metadata.ownerReferences. If the ExternalSecret is deleted, the Secret will also be deleted.
# - Merge: Does not create the Secret but merges data fields into the existing Secret (expects the Secret to already exist).
# - Orphan: Creates the Secret but does not set .metadata.ownerReferences. If the Secret already exists, it will be updated.
# - None: Does not create or update the Secret (reserved for future use with injector).
creationPolicy: Merge
# Specifies what happens to the Secret when data fields are deleted from the provider (e.g., Vault, AWS Parameter Store). Options:
# - Retain: (default) Retains the Secret if all Secret data fields have been deleted from the provider.
# - Delete: Removes the Secret if all Secret data fields from the provider are deleted.
# - Merge: Removes keys from the Secret but not the Secret itself.
deletionPolicy: Retain
# Specify a blueprint for the resulting Kind=Secret
template:
type: kubernetes.io/dockerconfigjson # or TLS...
metadata:
# Labels and annotations to set on the Secret.
# When a template is defined, these replace the default behavior
# of copying labels and annotations from the ExternalSecret.
# Set to an empty map ({}) to prevent any labels or annotations from being copied.
annotations: {}
labels: {}
# The finalizers will be added to the Secret.
# It is expected that finalizers will be deleted with custom cleanup functionality.
# This is required when another chart depends on the Secret, and it is needed to prevent the Secret from being deleted too early.
finalizers: []
# Use inline templates to construct your desired config file that contains your secret
data:
config.yml: |
database:
connection: postgres://{{ .username }}:{{ .password }}@{{ .database_host }}:5432/payments
# Uses an existing template from configmap
# Secret is fetched, merged and templated within the referenced configMap data
# It does not update the configmap, it creates a secret with: data["alertmanager.yml"] = ...result...
templateFrom:
- configMap:
name: application-config-tmpl
items:
- key: config.yml
# Data defines the connection between the Kubernetes Secret keys and the Provider data
data:
- secretKey: username
remoteRef:
key: database-credentials
version: v1
property: username
decodingStrategy: None # can be None, Base64, Base64URL or Auto
# define the source of the secret. Can be a SecretStore or a Generator kind
sourceRef:
# point to a SecretStore that should be used to fetch a secret.
# must be defined if no spec.secretStoreRef is defined.
storeRef:
name: aws-secretstore
kind: ClusterSecretStore
# Used to fetch all properties from the Provider key
# If multiple dataFrom are specified, secrets are merged in the specified order
# Can be defined using sourceRef.generatorRef or extract / find
# Both use cases are exemplified below
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: ECRAuthorizationToken
name: "my-ecr"
#Or
dataFrom:
- extract:
key: database-credentials
version: v1
property: data
conversionStrategy: Default
decodingStrategy: Auto
rewrite:
- regexp:
source: "exp-(.*?)-ression"
target: "rewriting-${1}-with-groups"
- find:
path: path-to-filter
name:
regexp: ".*foobar.*"
tags:
foo: bar
conversionStrategy: Unicode
decodingStrategy: Base64
rewrite:
- regexp:
source: "foo"
target: "bar"
status:
# refreshTime is the time and date the external secret was fetched and
# the target secret updated
refreshTime: "2019-08-12T12:33:02Z"
# Standard condition schema
conditions:
# ExternalSecret ready condition indicates the secret is ready for use.
# This is defined as:
# - The target secret exists
# - The target secret has been refreshed within the last refreshInterval
# - The target secret content is up-to-date based on any target templates
- type: Ready
status: "True" # False if last refresh was not successful
reason: "SecretSynced"
message: "Secret was synced"
lastTransitionTime: "2019-08-12T12:33:02Z"