Kubernetes Secrets are not secret. They are base64-encoded YAML stored in etcd. Anyone with RBAC access to read secrets in a namespace can decode them instantly. Etcd encryption at rest helps, but that only protects against someone stealing the etcd disk. It does nothing for the ten other ways secrets leak.
The ecosystem has produced several competing solutions. Each makes different tradeoffs. Picking the wrong one for your environment creates operational pain that leads teams to cut corners, which defeats the purpose.
The Problem Space
Kubernetes secrets management has four distinct challenges:
- Storage: Where does the secret material live at rest?
- Distribution: How do secrets get into the pods that need them?
- Rotation: How do you change secrets without downtime?
- Audit: Who accessed which secret and when?
No single tool solves all four equally well.
HashiCorp Vault
Vault is the most capable and most complex option. It is a dedicated secrets management platform with its own storage, access control, and audit logging.
Architecture
Vault runs as a separate service (or cluster) that pods authenticate to at runtime. Secrets never exist in Kubernetes YAML or etcd. The pod requests a secret from Vault, Vault validates the pod's identity (via Kubernetes service account JWT), and returns the secret directly.
Integration Patterns
Vault Agent Sidecar Injector: Injects a sidecar that authenticates to Vault and writes secrets to a shared volume:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "app-role"
vault.hashicorp.com/agent-inject-secret-db-password: "secret/data/db/password"
Vault CSI Provider: Mounts secrets via the Secrets Store CSI driver:
volumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: vault-db-password
Vault Secrets Operator (VSO): The newest approach. A Kubernetes operator that syncs Vault secrets to native Kubernetes Secrets:
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: db-password
spec:
vaultAuthRef: default
mount: secret
path: db/password
destination:
name: db-password
create: true
refreshAfter: 30s
Strengths
- Dynamic secrets (database credentials created on-demand with TTL)
- Fine-grained access policies
- Comprehensive audit logging
- Secret rotation built in
- Encryption as a service
Weaknesses
- Operational complexity. Vault itself needs HA, backup, unsealing, and monitoring.
- Latency. Every secret access is a network call.
- Learning curve. Vault's policy language, auth methods, and secret engines require dedicated expertise.
- Cost. HashiCorp Vault Enterprise pricing is significant. Open-source version lacks namespaces and replication.
Best For
Organizations with dedicated platform teams, compliance requirements for audit trails, and workloads that benefit from dynamic secrets (database-per-pod credentials, short-lived TLS certificates).
Sealed Secrets
Bitnami's Sealed Secrets takes the opposite approach to Vault. Instead of a runtime secret retrieval, it encrypts secrets at rest in Git.
Architecture
A cluster-side controller generates a public/private key pair. Developers encrypt secrets using the public key with kubeseal, producing a SealedSecret resource that can safely live in Git. The controller decrypts it and creates a regular Kubernetes Secret.
# Encrypt a secret
kubectl create secret generic db-password \
--from-literal=password=s3cret \
--dry-run=client -o json | \
kubeseal --controller-namespace kube-system \
--controller-name sealed-secrets \
-o yaml > sealed-db-password.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-password
spec:
encryptedData:
password: AgBxj3k8...encrypted...blob==
Strengths
- Secrets can live in Git repos (GitOps compatible)
- Simple mental model: encrypt locally, commit to Git, controller decrypts in cluster
- Low operational overhead—just the controller
- No external dependencies
Weaknesses
- No rotation mechanism. Changing a secret requires re-encrypting and redeploying.
- No audit trail beyond Git history.
- Cluster-bound encryption. Sealed secrets encrypted for cluster A cannot be decrypted by cluster B.
- Secret material still ends up as plain Kubernetes Secrets in etcd.
- Key rotation for the controller itself requires re-encrypting all sealed secrets.
Best For
Small to medium teams using GitOps who need a simple, low-overhead way to keep secrets out of plain-text YAML in Git repositories.
SOPS (Secrets OPerationS)
Mozilla's SOPS encrypts files (YAML, JSON, ENV, INI) using external key management services. It integrates with AWS KMS, GCP Cloud KMS, Azure Key Vault, age, and PGP.
Architecture
SOPS encrypts the values in a structured file while leaving keys and structure visible:
db:
password: ENC[AES256_GCM,data:abc123==,iv:xyz==,tag:def==,type:str]
host: ENC[AES256_GCM,data:ghi789==,iv:uvw==,tag:jkl==,type:str]
sops:
kms:
- arn: arn:aws:kms:us-east-1:123456789:key/abcd-1234
encrypted_regex: ^(password|host)$
Kubernetes Integration
SOPS alone does not create Kubernetes Secrets. You need a tool like Flux or ArgoCD to decrypt and apply:
# Flux Kustomization with SOPS decryption
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
spec:
decryption:
provider: sops
secretRef:
name: sops-age-key
Strengths
- Cloud-native KMS integration (secrets encrypted by your cloud provider's HSMs)
- File-level encryption means you can encrypt any structured file
- Multi-key support (multiple KMS keys, threshold schemes)
- Works outside Kubernetes too
Weaknesses
- Requires a GitOps tool for Kubernetes integration
- No runtime rotation. Same re-encrypt-and-deploy cycle as Sealed Secrets.
- Partial encryption can confuse tools that parse the files.
- Key management shifts to the KMS provider.
Best For
Teams already using cloud KMS and GitOps (Flux or ArgoCD) who want to encrypt Kubernetes manifests alongside other configuration files.
External Secrets Operator
ESO is a Kubernetes operator that synchronizes secrets from external providers into Kubernetes Secrets. It supports AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, HashiCorp Vault, and a dozen other backends.
Architecture
You define an ExternalSecret resource that references a secret in an external store. The operator fetches it and creates a Kubernetes Secret:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-password
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: db-password
data:
- secretKey: password
remoteRef:
key: prod/db/password
Strengths
- Provider-agnostic. Swap backends without changing application code.
- Automatic refresh. Secrets update on a schedule without redeployment.
- Template support. Transform and combine secrets from multiple sources.
- Mature project with broad community adoption.
Weaknesses
- Secrets still materialize as Kubernetes Secrets in etcd.
- Depends on network connectivity to external providers.
- Refresh interval means secrets are eventually consistent, not immediately consistent.
- Operator needs broad permissions to create secrets across namespaces.
Best For
Multi-cloud organizations that want a single Kubernetes interface for secrets from diverse backends, with automatic refresh and no application-level changes.
Decision Matrix
| Requirement | Vault | Sealed Secrets | SOPS | ESO | |---|---|---|---|---| | Dynamic secrets | Yes | No | No | Depends on backend | | GitOps compatible | Via VSO | Yes | Yes | Yes | | Audit trail | Built-in | Git only | Git only | Backend-dependent | | Rotation | Automated | Manual | Manual | Automated refresh | | Operational overhead | High | Low | Low | Medium | | Multi-cluster | Yes | Per-cluster | Yes (with KMS) | Yes | | Avoids etcd storage | Sidecar/CSI modes | No | No | No |
The Hybrid Approach
Most mature teams combine tools:
- Vault for dynamic database credentials and PKI
- External Secrets Operator for static configuration secrets from cloud secret managers
- SOPS for encrypting non-secret configuration that still should not be plain text in Git
This is not overengineering. It is matching the tool to the secret type.
How Safeguard.sh Helps
Safeguard.sh audits your Kubernetes secrets management posture across clusters. It identifies secrets stored without encryption at rest, flags overly broad RBAC permissions for secret access, detects secrets exposed through environment variables (which appear in logs and process listings), and verifies that your chosen secrets management solution is properly configured. The platform tracks secret rotation compliance and alerts when secrets exceed their maximum age, ensuring your management tooling is not just installed but actively maintaining your security posture.