GitOps says everything in Git. Secrets say otherwise. External Secrets Operator bridges the gap: secret definitions live in Git, actual values live in your secrets manager.
The Problem
# This is what you want in Git:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
data:
password: cGFzc3dvcmQxMjM= # ← Base64 is not encryptionSealed Secrets encrypts them. External Secrets takes a different approach: store nothing sensitive in Git. Reference secrets by name, fetch values at runtime.
Installation
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets --create-namespaceConnect to a Secret Store
AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secrets
spec:
provider:
aws:
service: SecretsManager
region: eu-west-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
namespace: external-secretsHashiCorp Vault
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault
spec:
provider:
vault:
server: https://vault.myorg.com
path: secret
version: v2
auth:
kubernetes:
mountPath: kubernetes
role: external-secretsAzure Key Vault
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: azure-kv
spec:
provider:
azurekv:
tenantId: "your-tenant-id"
vaultUrl: "https://myorg-kv.vault.azure.net"
authType: WorkloadIdentity
serviceAccountRef:
name: external-secrets-saMaster this topic with hands-on labs
Go beyond reading — build real projects in sandboxed environments with expert video guidance.
Browse Courses →Create an External Secret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: production/database
property: username
- secretKey: password
remoteRef:
key: production/database
property: passwordThe operator creates a standard Kubernetes Secret named db-credentials with values fetched from AWS Secrets Manager. Pods consume it normally:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: passwordWhat Lives in Git vs What Does Not
In Git (ExternalSecret YAML): In AWS/Vault (actual values):
- Secret name: db-credentials - username: app_user
- Source: production/database - password: s3cur3-p@ssw0rd
- Refresh: every 1 hour - host: db.internal.myorg.com
- Target namespace: productionGit contains the reference. The secrets manager contains the value.
Secret Rotation
spec:
refreshInterval: 15m # Check for updates every 15 minutesRotate the secret in AWS Secrets Manager → External Secrets Operator picks up the new value within 15 minutes → Kubernetes Secret is updated → pods restart (if configured).
For zero-downtime rotation, combine with the Reloader controller:
metadata:
annotations:
reloader.stakater.com/auto: "true"Get weekly IT automation tips
Docker, Ansible, Terraform, MLOps — curated insights delivered to your inbox. No spam.
Subscribe Free →Template Secrets
Generate complex secret formats:
spec:
target:
name: db-connection
template:
engineVersion: v2
data:
connection_string: "postgresql://{{ .username }}:{{ .password }}@{{ .host }}:5432/{{ .database }}"
data:
- secretKey: username
remoteRef:
key: production/database
property: username
- secretKey: password
remoteRef:
key: production/database
property: password
- secretKey: host
remoteRef:
key: production/database
property: host
- secretKey: database
remoteRef:
key: production/database
property: databaseThe resulting Kubernetes Secret contains a fully-formed connection string.
Multi-Source Secrets
Pull from different secret stores into one Kubernetes Secret:
spec:
data:
- secretKey: db-password
remoteRef:
key: production/database
property: password
sourceRef:
storeRef:
name: aws-secrets
kind: ClusterSecretStore
- secretKey: api-key
remoteRef:
key: secret/data/api-keys
property: stripe
sourceRef:
storeRef:
name: vault
kind: ClusterSecretStoreExternal Secrets vs Alternatives
| Feature | External Secrets | Sealed Secrets | Vault Agent |
|---|---|---|---|
| Secret in Git | Reference only | Encrypted blob | No |
| Secret store | Any (AWS, Vault, Azure, GCP) | Cluster-only | Vault only |
| Rotation | Automatic | Manual re-encrypt | Automatic |
| Complexity | Medium | Low | High |
| GitOps compatible | Yes | Yes | Partial |
Use External Secrets when you already have a secrets manager and want GitOps. Use Sealed Secrets for simpler setups without an external secrets manager.
---
Ready to go deeper? Master Kubernetes security with hands-on courses at CopyPasteLearn.
Ready to learn by doing?
Stop reading tutorials — start building. Expert video courses with hands-on labs in real sandboxed environments.
Related Articles
Crossplane Infrastructure as Code
Crossplane lets you manage cloud infrastructure using Kubernetes custom resources. Learn how it works, how it compares to Terraform, and when to choose.
Karpenter Kubernetes Autoscaler
Karpenter provisions the right Kubernetes nodes in seconds, not minutes. Learn how it replaces Cluster Autoscaler with faster, smarter node provisioning.
Flux GitOps Kubernetes Tutorial
Flux is a GitOps tool that continuously reconciles your Kubernetes cluster with a Git repository. Learn how to set up Flux, manage Helm releases, and handle.
Falco Runtime Security Kubernetes
Falco detects runtime threats in Kubernetes using eBPF. Learn how to set up Falco for container security monitoring, write custom rules, and integrate.
Fedora 43: Modern Linux Desktop
Fedora 43 delivers cutting-edge packages with GNOME 48, Wayland by default, and a polished developer experience for desktop Linux.
FinOps for Engineers Practical Guide
FinOps is not just for finance teams. Learn how engineers can reduce cloud costs with resource tagging, right-sizing, committed use discounts, and automated.
Explore topics
Browse more articles on the topics covered here.