Kubernetes Secrets Are Not Secret
kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 -d
That’s it. Base64 is encoding, not encryption. Anyone with RBAC read access to the namespace can see every secret. In most clusters, that’s way too many people.
You need a secrets management solution. Here are the real options.
Option 1: HashiCorp Vault
The gold standard. Full-featured, battle-tested, complex.
# Install Vault on Kubernetes
helm install vault hashicorp/vault \
--set server.ha.enabled=true \
--set server.ha.raft.enabled=true \
--namespace vaultVault Agent Sidecar (automatic injection)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-app"
vault.hashicorp.com/agent-inject-secret-db: "secret/data/db/credentials"
spec:
containers:
- name: my-app
# Secret available at /vault/secrets/dbWhen to Choose Vault
- Multi-cloud or hybrid environments
- Dynamic secrets (database credentials that rotate automatically)
- PKI / certificate management
- Strict compliance requirements (FIPS, SOC2)
- Large organizations with dedicated platform teams
The Complexity Tax
Vault is operationally expensive. HA setup, unsealing, token management, policy administration. I’ve seen teams spend 20% of their platform engineering time managing Vault itself.
Option 2: External Secrets Operator (ESO)
ESO syncs secrets FROM your cloud provider’s secret store INTO Kubernetes secrets:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: db-credentials
data:
- secretKey: username
remoteRef:
key: production/db/credentials
property: username
- secretKey: password
remoteRef:
key: production/db/credentials
property: passwordSecretStore Configuration
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: eu-west-1
auth:
jwt:
serviceAccountRef:
name: external-secrets
namespace: external-secretsWhen to Choose ESO
- Already using a cloud provider’s secret store
- Want GitOps-friendly secrets management
- Simpler operational overhead than Vault
- Multi-cluster with centralized secrets
Comparison
Vault ESO Sealed Secrets
Complexity High Medium Low
Dynamic secrets Yes No (sync only) No
Rotation Automatic Manual + sync Manual
Cloud-agnostic Yes Via providers Yes
GitOps friendly Limited Yes Yes
Operational cost High Low Minimal
Best for Enterprise Cloud-native Small teamsMy Recommendation
For most Kubernetes deployments:
Small team (1-20 devs):
→ Cloud provider secret store + ESO
→ Simple, managed, affordable
Medium team (20-100 devs):
→ ESO + cloud secret store for most secrets
→ Vault for dynamic secrets (DB creds) if needed
Large enterprise (100+ devs):
→ Vault for centralized secrets management
→ ESO as the Kubernetes integration layer
→ Cloud KMS for encryption keysAutomating Secrets Infrastructure
I deploy the secrets management stack with Ansible:
- name: Deploy secrets management
hosts: k8s_clusters
tasks:
- name: Install External Secrets Operator
helm:
name: external-secrets
chart_ref: external-secrets/external-secrets
release_namespace: external-secrets
create_namespace: true
- name: Configure ClusterSecretStore
kubernetes.core.k8s:
state: present
template: cluster-secret-store.yml.j2The infrastructure provisioning (IAM roles, KMS keys, secret store setup) I handle with Terraform — patterns at Terraform Pilot. The Kubernetes-side automation with Ansible at Ansible Pilot.
Whatever you choose — stop using plain Kubernetes secrets in production. The base64 “encryption” is a false sense of security that will bite you.
