How-To Guide
Configure External Secret Stores¶
This guide shows you how to configure External Secrets Operator (ESO) to sync secrets from external sources into Kubernetes.
Prerequisites¶
External Secrets Operator installed (included in infrastructure tier)
kubectl access to the cluster
Credentials for your external secret backend
Create a SecretStore¶
A SecretStore defines how to access an external secret backend.
Example: Kubernetes Secret Store (Cross-Cluster Sync)¶
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: kubernetes-backend
namespace: my-app
spec:
provider:
kubernetes:
# For remote cluster access
remoteNamespace: source-namespace
server:
url: "https://other-cluster.example.com"
caProvider:
type: ConfigMap
name: kube-root-ca.crt
key: ca.crt
auth:
token:
bearerToken:
name: kubernetes-token
key: token
Example: Webhook Provider (Generic Backend)¶
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: webhook-backend
namespace: my-app
spec:
provider:
webhook:
url: "https://secret-api.example.com/secrets/{{ .remoteRef.key }}"
result:
jsonPath: "$.data"
headers:
Authorization:
secretRef:
name: webhook-credentials
key: token
Create an ExternalSecret¶
An ExternalSecret defines which secrets to fetch and how to create Kubernetes secrets.
Basic Example¶
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-app-secrets
namespace: my-app
spec:
# Refresh interval
refreshInterval: 1h
# Reference to SecretStore
secretStoreRef:
name: kubernetes-backend
kind: SecretStore
# Target Kubernetes secret
target:
name: my-app-secrets
creationPolicy: Owner
# Data to fetch
data:
- secretKey: database-password
remoteRef:
key: db-credentials
property: password
- secretKey: api-key
remoteRef:
key: api-credentials
property: key
Template Example (Generate Secrets)¶
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: connection-string
namespace: my-app
spec:
refreshInterval: 1h
secretStoreRef:
name: kubernetes-backend
kind: SecretStore
target:
name: postgres-connection
template:
type: Opaque
data:
# Template a connection string from multiple secrets
connection-string: |
postgresql://{{ .username }}:{{ .password }}@{{ .host }}:5432/{{ .database }}
dataFrom:
- extract:
key: postgres-credentials
Create a ClusterSecretStore¶
Use ClusterSecretStore for cluster-wide secret stores (accessible from all namespaces).
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: global-secrets
spec:
provider:
kubernetes:
remoteNamespace: global-secrets
auth:
serviceAccount:
name: external-secrets
Verify Secret Synchronization¶
Check ExternalSecret status:
# Check if ExternalSecret is syncing
kubectl get externalsecret my-app-secrets -n my-app
# View detailed status
kubectl describe externalsecret my-app-secrets -n my-app
# Check generated Kubernetes secret
kubectl get secret my-app-secrets -n my-app -o yaml
Expected output for healthy ExternalSecret:
NAME STORE REFRESH INTERVAL STATUS READY
my-app-secrets kubernetes-backend 1h SecretSynced True
Troubleshooting¶
ExternalSecret shows “SecretSyncedError”¶
Check SecretStore configuration:
kubectl describe secretstore kubernetes-backend -n my-app
Common issues:
Invalid credentials in authentication secret
Network connectivity to external backend
Incorrect remote key path
Secret not updating after external change¶
Check refresh interval:
kubectl get externalsecret my-app-secrets -n my-app -o jsonpath='{.spec.refreshInterval}'
Force immediate refresh by deleting and recreating ExternalSecret (secret will be regenerated).
Security Best Practices¶
Namespace Isolation: Use SecretStore (namespaced) instead of ClusterSecretStore unless cluster-wide access is required
Least Privilege: Grant minimal permissions to ESO service account for accessing external backends
Refresh Intervals: Balance between freshness and API rate limits (recommended: 1h-24h)
Secret Rotation: Use short-lived tokens when possible, leverage ESO’s refresh to propagate rotations
Audit Logs: Monitor ExternalSecret events for unauthorized access attempts
Common Patterns¶
Pattern: Environment-Specific Secrets¶
# dev-secrets (SecretStore)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: dev-secrets
namespace: my-app
spec:
provider:
kubernetes:
remoteNamespace: dev-environment
---
# prod-secrets (SecretStore)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: prod-secrets
namespace: my-app
spec:
provider:
kubernetes:
remoteNamespace: prod-environment
Reference by environment in deployment:
spec:
secretStoreRef:
name: {{ .Values.environment }}-secrets
Pattern: Multi-Source Secret Aggregation¶
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: aggregated-config
spec:
refreshInterval: 30m
secretStoreRef:
name: kubernetes-backend
target:
name: app-config
# Fetch from multiple sources
dataFrom:
- extract:
key: common-config
- extract:
key: app-specific-config
Pattern: Application Secrets with Namespace Isolation (Recommended)¶
For production applications with multiple secrets, use the application-secrets namespace pattern. This provides a secure, scalable approach for managing application secrets without committing them to Git.
Key benefits:
✅ No secrets in Git (only ExternalSecret configs)
✅ Centralized secret rotation (update once, propagate everywhere)
✅ Namespace isolation via RBAC
✅ Scales to multiple applications
✅ GitOps-friendly (declarative resources)
Architecture:
application-secrets namespace (source)
├─ Source secrets (bootstrap once)
└─ ServiceAccount + RBAC (read-only)
↓
ClusterSecretStore (namespace restricted)
↓
Application namespace (target)
├─ ExternalSecrets (declarative, in Git)
└─ Synced secrets (auto-managed by ESO)
When to use:
Application has multiple secrets (>3)
Need centralized secret rotation
Want to avoid Git commits of secrets
Require namespace isolation
Multiple environments (dev/staging/prod)
Example implementation: GitLab BDA deployment uses this pattern for 15+ secrets across 3 ExternalSecrets.
Learn more:
How-To: Bootstrap Application Secrets - Complete implementation guide
Explanation: Application Secrets Architecture - Design rationale
Reference: GitLab BDA Secrets - Production example
Next Steps¶
For HashiCorp Vault integration, see Configure Vault Secret Store
For AWS Secrets Manager (S3-compatible), see Configure AWS Secret Store
For understanding ESO architecture, see Explanation: External Secrets Operator