A container to be used as Kubernetes Job that triggers ExternalSecrets to refresh by patching their annotations and monitors the refresh status.
To be used for example in Helm Charts or ArgoCD applications.
- Identifies ExternalSecrets based on namespace and label selectors
- Starts watching for status changes before triggering refreshes to capture all events
- Patches each ExternalSecret with a
force-syncannotation containing a UTC timestamp - Monitors status updates, checking for updated
refreshTimeandReadycondition - Exit code reflects overall success (0 = all refreshed, 1 = failures)
The job executes the following workflow to ensure reliable ExternalSecret refreshes:
- Initialization: Configures structured logging and loads Kubernetes configuration (in-cluster or local).
- Discovery: Lists all ExternalSecrets in the specified namespace(s) matching the label selector.
- Baseline Recording: Captures the current
refreshTimefrom each ExternalSecret's status for comparison. - Watch Setup: Initiates a Kubernetes watch on ExternalSecrets to monitor real-time status changes.
- Trigger Refresh: Patches each ExternalSecret with a
force-syncannotation set to the current UTC timestamp. - Progress Monitoring: Processes watch events to detect successful refreshes:
- Checks for updated
refreshTime(newer than baseline) - Verifies the
Readystatus condition isTrue
- Checks for updated
- Completion: Continues monitoring until all ExternalSecrets refresh or the 60-second timeout expires.
- Result Evaluation: Exits with code 0 if all succeeded, otherwise exits with 1.
docker build -t externalsecrets-refresh:latest .The job can be configured via environment variables:
NAMESPACE: Target namespace (defaults to job's namespace). Leave unset for all namespaces.LABEL_SELECTOR: Label selector to filter ExternalSecrets (e.g.,app=myapp,env=prod)EXTERNALSECRETS_VERSION: API version for ExternalSecrets (default:v1)
Deploy the job:
kubectl apply -f job.yamlCustomize for specific namespace:
env:
- name: NAMESPACE
value: "production"
- name: LABEL_SELECTOR
value: "app=myapp"# Check job status
kubectl get jobs externalsecrets-refresh
# View logs
kubectl logs -f job/externalsecrets-refresh
# Check exit code
kubectl get job externalsecrets-refresh -o jsonpath='{.status.conditions[?(@.type=="Failed")].reason}'The job requires:
get,list,watch,patchonexternalsecretsget,watchonexternalsecrets/status
Two RBAC configurations are provided:
- Role/RoleBinding: For single namespace access (default)
- ClusterRole/ClusterRoleBinding: For cluster-wide access (commented out in job.yaml)
To trigger this job during Helm deployment, add it as a pre-install hook:
apiVersion: batch/v1
kind: Job
metadata:
name: externalsecrets-refresh annotations:
helm.sh/hook: pre-install
helm.sh/hook-weight: "-5"
helm.sh/hook-delete-policy: before-hook-creation
spec:
# ... rest of job spec0: All ExternalSecrets refreshed successfully1: One or more ExternalSecrets failed to refresh
No ExternalSecrets found:
- Check namespace and label selector configuration
- Verify RBAC permissions
Timeout after 60 seconds:
- Check ExternalSecret controller is running
- Verify external secret store connectivity
- Review ExternalSecret status conditions for errors
Permission denied:
- Ensure ServiceAccount has proper RBAC permissions
- For cluster-wide access, use ClusterRole/ClusterRoleBinding