Kubernetes Secrets are key-value pairs stored in etcd as base64-encoded strings - NOT encrypted by default. The official docs are clear: enabling encryption at rest is a separate flag (--encryption-provider-config). The team that treats Secrets as encrypted has a security incident. The team that enables encryption, uses RBAC, and pulls secrets from an external manager (Vault, AWS Secrets Manager, External Secrets Operator) has the right posture.
Table of contents
- What a Secret is
- Why base64 isn’t encryption
- Encryption at rest
- RBAC for Secrets
- Mounting Secrets as env vars or files
- External secret managers
- FAQ
What a Secret is
apiVersion: v1
kind: Secret
metadata:
name: db-creds
type: Opaque
stringData:
username: deploy
password: hunter2
This is base64-encoded in etcd. Anyone with get secret RBAC can decode it. The team that thinks ‘Secret = encrypted’ is wrong. The team that uses external secret managers doesn’t store the real secret in etcd at all.
Why base64 isn’t encryption
Base64 is encoding, not encryption. It’s reversible without a key. echo aGFja2Vy | base64 -d gives back ‘hacker’. The team that uses Secrets as plaintext storage (which is what they are) treats them with appropriate care.
Encryption at rest
To actually encrypt Secrets in etcd:
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
Then update kube-apiserver flags with --encryption-provider-config. The team that runs production clusters enables this - the team that skips it has plaintext secrets in etcd backups.
RBAC for Secrets
Limit who can read Secrets:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app
name: secret-reader
rules:
- apiGroups: ['']
resources: ['secrets']
verbs: ['get']
The team that gives everyone in the namespace get secrets has every dev able to read production credentials. The team that uses RBAC + per-namespace service accounts has least-privilege.
Mounting Secrets as env vars or files
Two patterns:
# As env vars
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-creds
key: password
# As files
volumes:
- name: secret-vol
secret:
secretName: db-creds
containers:
- volumeMounts:
- name: secret-vol
mountPath: /etc/secrets
The team that uses file mounts has env vars that show up in kubectl exec (env vars leak easily). The team that uses file mounts at /var/run/secrets/ has better hygiene.
External secret managers
Real production: don’t store secrets in K8s at all. Use:
- HashiCorp Vault: pull secrets via Vault Agent or Vault CSI provider.
- AWS Secrets Manager + External Secrets Operator: sync from AWS to K8s.
- GCP Secret Manager: similar pattern.
- Sealed Secrets (Bitnami): encrypted Secrets safe to commit to git.
The team that uses Vault + Vault Agent Injector has dynamic, short-lived credentials. The team that uses Sealed Secrets has gitops-friendly encrypted secrets. The team that uses raw K8s Secrets with encryption-at-rest enabled has good-enough hygiene for many teams.
FAQ
Are Kubernetes Secrets encrypted at rest by default?
No. They are base64-encoded plaintext in etcd. The team that needs encryption at rest enables --encryption-provider-config on kube-apiserver.
Can I commit Secrets to git if I use Sealed Secrets?
Yes - Sealed Secrets are encrypted with a cluster key, safe to commit to git. The team that uses Sealed Secrets has gitops-friendly secret management.
What’s the difference between Secret and ConfigMap?
Both are key-value. Secrets are meant for sensitive data (often base64, optionally encrypted). ConfigMaps are for non-sensitive config. The team that uses ConfigMap for API URLs and Secret for credentials has the right separation.
Should I use Vault or just K8s Secrets?
Vault for production with strict compliance requirements. K8s Secrets with encryption-at-rest and RBAC for most teams. The team that uses Vault has centralized audit + dynamic credentials; the team that uses K8s Secrets has lower complexity.
How do I rotate a Secret?
Update the Secret’s data, then either: (1) restart pods that mount it, (2) use a sidecar that watches and reloads, or (3) use external secret managers that handle rotation. The team that uses Vault Agent has automatic rotation; the team that uses raw K8s Secrets does it manually.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: