Secret: Stores sensitive data (passwords, tokens, TLS certs). Values are base64-encoded, NOT encrypted by default.
Both can be injected as environment variables OR mounted as files into a pod.
Storage: Both are stored in etcd. Secrets have an additional encryption-at-rest layer (envelope encryption) that must be explicitly enabled.
Env vars: Simple, but require pod restart for updates. Visible in pod spec and process environment.
Volume mounts: Support live rotation without restart. File permissions can be restricted.
Assuming base64 encoding equals encryption. It does not. Anyone with API access can decode a Secret. Envelope encryption must be configured separately via EncryptionConfiguration.
✦ Definition~90s read
What is Kubernetes ConfigMaps and Secrets?
Kubernetes ConfigMaps and Secrets are core API objects that externalize configuration from container images. ConfigMaps hold non-sensitive data: environment-specific URLs, feature flags, configuration files. Secrets hold sensitive data: passwords, API tokens, TLS private keys. Both are namespaced resources, stored in etcd, and can be consumed by pods as environment variables or mounted volumes.
★
Imagine your app is a coffee machine.
Plain-English First
Imagine your app is a coffee machine. The machine itself is the same every time — but the recipe card telling it how strong to brew, and the locked safe holding the Wi-Fi password, those change depending on the kitchen it's in. ConfigMaps are the recipe card: non-sensitive settings anyone can read. Secrets are the locked safe: passwords and keys that only authorized hands should touch. Kubernetes separates these two so you never accidentally display your database password in plain sight on a Post-it note.
⚙ Browser compatibility
Latest versions — ✓ supported
Chrome
Firefox
Safari
Edge
✓
✓
✓
✓
Every production Kubernetes cluster eventually hits the same wall: the app image is beautifully immutable, but the configuration it needs — database URLs, feature flags, TLS certificates, API keys — changes constantly across environments. Baking config into the image means rebuilding for a one-line change. Passing it ad-hoc at runtime means no audit trail and no consistency across hundreds of pods.
Kubernetes solves this with two first-class API objects: ConfigMaps for ordinary configuration data and Secrets for sensitive credentials. Both decouple config from the container image, but they have fundamentally different storage mechanisms, access controls, and risk profiles. Understanding that difference — not just syntactically, but at the etcd and kubelet level — is what separates engineers who use these safely from engineers who accidentally expose credentials in pod specs.
This is not a syntax reference. It is for engineers who need to understand how ConfigMaps and Secrets are persisted, why base64 is NOT encryption, when to mount as a file versus inject as an environment variable and why it matters for rotation, how to enable envelope encryption at rest, and the RBAC patterns that keep least-privilege real in a multi-team cluster.
What is Kubernetes ConfigMaps and Secrets?
Kubernetes ConfigMaps and Secrets are core API objects that externalize configuration from container images. ConfigMaps hold non-sensitive data: environment-specific URLs, feature flags, configuration files. Secrets hold sensitive data: passwords, API tokens, TLS private keys. Both are namespaced resources, stored in etcd, and can be consumed by pods as environment variables or mounted volumes.
ConfigMap and Secret created in 'production' namespace.
Mental Model
Base64 Is Not Encryption
Think of base64 like putting a letter in an envelope. Anyone who gets the envelope can open it. The security comes from controlling who receives the envelope.
base64 is reversible with a single command: echo <value> | base64 -d.
Security relies on RBAC: who can get or list Secrets.
Encryption at rest (envelope encryption) protects against etcd data theft, not API access.
For real encryption, use external secret stores (Vault, AWS Secrets Manager) with the Secrets Store CSI Driver.
📊 Production Insight
Every Secret in the cluster is readable by any entity with get permission on the secrets resource in that namespace. In practice, this means: cluster admins can see all secrets, CI/CD service accounts often have broad secret access, and compromised pods with mounted service account tokens can potentially read other secrets. Mitigate with: dedicated service accounts per workload, minimal RBAC roles, and disabling automounting of service account tokens (automountServiceAccountToken: false).
🎯 Key Takeaway
ConfigMaps and Secrets solve the configuration management problem by decoupling config from images. But they are not a security boundary. The real security comes from RBAC, network policies, and optionally envelope encryption. Treat Secret access as a privilege, not a default.
thecodeforge.io
Kubernetes Configmaps Secrets
Storage Internals: How etcd Handles ConfigMaps and Secrets
Both ConfigMaps and Secrets are stored as key-value pairs in etcd through the API Server. The API Server is the only component that talks to etcd directly. When you create a ConfigMap or Secret, the API Server validates it, serializes it, and writes it to etcd. When a kubelet needs to mount a Secret into a pod, it fetches the data from the API Server (which reads from etcd).
# EnvelopeEncryptionConfigurationforSecrets at Rest
# This file goes on the APIServer as --encryption-provider-config
# Package: io.thecodeforge.kubernetes
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets # EncryptSecrets at rest
- configmaps # Optionally encrypt ConfigMaps too
providers:
- aescbc: # Localencryption (AES-CBC)
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {} # Fallback: no encryption (reads old unencrypted data)
Output
API Server configured to encrypt Secrets at rest in etcd.
Mental Model
Envelope Encryption: What It Actually Does
Without envelope encryption, anyone with filesystem access to etcd nodes can read Secrets in plain text.
Default: Secrets are stored in plain text in etcd.
Envelope encryption is opt-in via --encryption-provider-config flag on the API Server.
Key rotation: Add a new key to the config, restart API Server, then run kubectl get secrets -A -o json | kubectl replace -f - to re-encrypt.
Cloud KMS integration (e.g., AWS KMS) means you never handle the KEK directly.
📊 Production Insight
Without envelope encryption, a compromised etcd backup or a disk from a decommissioned etcd node exposes every Secret in the cluster history. This is not theoretical — it is a common finding in security audits. Enable envelope encryption on day one. Use cloud KMS for the KEK so key rotation is handled by the cloud provider. Monitor etcd's etcd_debugging_mvcc_db_total_size_in_bytes metric; large Secrets (e.g., multi-megabyte TLS certs) inflate etcd and degrade performance.
🎯 Key Takeaway
etcd is the persistence layer for all Kubernetes objects, including Secrets. By default, Secrets are stored unencrypted. Envelope encryption with a KMS-backed KEK is the production standard. Treat etcd backups with the same sensitivity as the Secrets themselves.
Env Var vs Volume Mount: The Rotation Trade-off
ConfigMaps and Secrets can be consumed by pods in two ways: as environment variables or as mounted files. This choice has significant implications for secret rotation, visibility, and application architecture.
Pods configured with both env-var and volume-mount patterns.
Mental Model
The Rotation Problem
This is why volume mounts are the preferred pattern for credentials that rotate frequently.
Env vars: Simple, but require pod restart for updates. Values visible in kubectl describe pod.
Volume mounts: Support live rotation (~60s kubelet sync). Values NOT visible in pod spec.
subPath mounts: Do NOT auto-update. Avoid for rotating secrets.
Application must watch or periodically re-read mounted files to detect changes.
📊 Production Insight
The kubelet's Secret/ConfigMap sync interval is controlled by --sync-frequency (default 1 minute). This means there is up to a 60-second window where a pod has stale credentials after a Secret update. For security-critical rotations (e.g., revoking a compromised key), this delay is unacceptable. In those cases, perform a rolling pod restart immediately after updating the Secret, rather than relying on the kubelet sync.
🎯 Key Takeaway
Choose volume mounts for any credential that rotates. Choose env vars only for truly static configuration. Never use subPath for rotating secrets. Design your application to re-read mounted files, not just read them once at startup.
thecodeforge.io
Kubernetes Configmaps Secrets
RBAC and Least Privilege for Secrets
Secret access must be tightly controlled via RBAC. In a multi-team cluster, the default behavior — where any pod's service account can potentially read any Secret in its namespace — is a security anti-pattern.
# ServiceAccount: Dedicated per workload
apiVersion: v1
kind: ServiceAccount
metadata:
name: payment-service-account
namespace: payments
automountServiceAccountToken: false # Disable unless the app needs API access
---
# Role: Read only specific Secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: payments
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["payment-gateway-key", "db-credentials"] # Only specific secrets
verbs: ["get"] # NOT'list' — prevents enumeration
---
# RoleBinding: BindSA to Role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: payment-secret-access
namespace: payments
subjects:
- kind: ServiceAccount
name: payment-service-account
namespace: payments
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
Output
Least-privilege RBAC for Secret access configured.
Mental Model
The Service Account Token Problem
This is why automountServiceAccountToken: false should be your default unless the pod explicitly needs Kubernetes API access.
Default service account has broad permissions in many clusters.
Compromised pod + mounted token = potential Secret enumeration.
Set automountServiceAccountToken: false on all pods that do not need API access.
Use resourceNames in RBAC to restrict which specific Secrets a service account can read.
Avoid list verb on Secrets — it allows enumeration of all Secret names.
📊 Production Insight
In multi-tenant clusters, consider using network policies to block pod-to-API-Server traffic for pods that do not need it. Combine this with OPA/Gatekeeper or Kyverno policies that enforce automountServiceAccountToken: false as a cluster-wide default. Audit RBAC regularly: kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa> shows exactly what a service account can do.
🎯 Key Takeaway
RBAC is the real security boundary for Secrets. Use dedicated service accounts per workload, restrict to specific Secret names, disable automounting, and never grant list on Secrets unless absolutely necessary. Treat every service account token as a potential attack vector.
Secrets Store CSI Driver: The bridge. Mounts external secrets as files without storing them in etcd.
External Secrets Operator: Alternative approach. Syncs external secrets INTO native Kubernetes Secrets.
📊 Production Insight
The Secrets Store CSI Driver mounts secrets directly from the external store into the pod as files. The secret never touches etcd. This eliminates the etcd encryption concern entirely. However, it adds a runtime dependency: if Vault is down, new pods cannot start. Mitigate with: Vault HA clusters, local caching in the CSI driver (--set syncSecret.enabled=true), and fallback logic in your application.
🎯 Key Takeaway
External secret stores are the production standard for mature organizations. The Secrets Store CSI Driver is the integration layer. The trade-off is operational complexity and a new failure mode (external store availability). Plan for it.
Consuming ConfigMaps: Environment Variables vs. Volume Mounts
Creating a ConfigMap is just the first step. The real decision is how your pod consumes it. Two paths exist: environment variables and volume mounts. Environment variables are simple—inject key-value pairs directly into your container's env. But they're static. Change the ConfigMap? Pods don't care. You must restart every pod to pick up new values. Volume mounts offer live updates. Mount a ConfigMap as a volume, and Kubernetes syncs changes to the file system (with a delay). Your app can watch for file changes and reconfigure without restarting. This matters for zero-downtime config changes. I've seen teams restart hundreds of pods unnecessarily because they picked env vars over volumes. Pick volumes for dynamic config, env vars for startup-only values. Never mix both for the same data source—it's a debugging nightmare.
Volume mount sync is eventually consistent. In a rolling update, old pods may read stale values for up to 2 minutes. If you need instant consistency, use a sidecar that watches the ConfigMap and signals your app. Or—better—bake the config into your deployment and roll pods cleanly.
🎯 Key Takeaway
Volume mounts for live config changes; env vars for immutable startup configuration only.
Secrets Are Not Encrypted by Default—Secure etcd Immediately
Here's a truth that keeps me up at night: Kubernetes Secrets are base64 encoded, not encrypted. Base64 is not a security measure. It's just obfuscation. Anyone with access to etcd—backups, snapshots, direct API access—can decode every Secret in your cluster. The default behavior is dangerous. You must enable encryption at rest for etcd immediately. This is a one-time cluster configuration that encrypts Secret data before writing to disk. Without it, a compromised etcd backup leaks all your database passwords and API keys. I've audited clusters where Secrets sat in plaintext for years. Don't be that team. Also, always restrict Secret access with RBAC. A developer reading production database credentials should trigger an alert. Treat Secrets like loaded guns: secure the storage, lock the trigger, and log every pull.
encryption-config.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: $(openssl rand -base64 32)
- identity: {}
# Verify encryption is active:
# $ kubectl get secret my-secret -o yaml | grep -A1'data:'
# data:
# password: <base64><-- still base64 in API, but encrypted at rest in etcd
# Check etcd directly:
# $ ETCDCTL_API=3 etcdctl get /registry/secrets/default/my-secret | hexdump
# First bytes show "k8s:enc:aescbc:v1:key1"if encrypted
Output
k8s:enc:aescbc:v1:key1...
🔥Production Hardening:
Enable etcd encryption before any Secret is created. Retroactive encryption requires re-creating Secrets. Also rotate encryption keys yearly—it's a simple kubectl apply of a new key and restarting the API server. Automate this in your cluster bootstrap.
🎯 Key Takeaway
Base64 is not encryption. Enable etcd encryption at rest before you create your first Secret.
thecodeforge.io
Kubernetes Configmaps Secrets
External Secret Stores: Why Native Secrets Fall Short at Scale
Native Kubernetes Secrets are convenient for small teams. But at scale, they break. No automatic rotation. No audit trail. No integration with cloud vaults. Teams end up storing secrets in Git, syncing them manually, and missing rotation deadlines. This is where external secret operators shine—like External Secrets Operator, AWS Secrets Manager CSI Driver, or HashiCorp Vault. These tools pull secrets from a centralized store (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Vault) and sync them into Kubernetes Secrets automatically. The critical advantage: rotation. When you rotate a secret in the vault, the operator updates the Kubernetes Secret, and mounted volumes pick it up. No pod restarts. I run External Secrets Operator in production for exactly this reason. It decouples secret lifecycle from application lifecycle. Warning: don't give every namespace access to every vault path. Use namespace annotations and strict IAM roles to isolate access.
externalsecret.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: "1h"
secretStoreRef:
name: aws-secretsmanager
kind: SecretStore
target:
name: db-secret
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: prod/database/primary
property: password
# After rotation in AWSSecretsManager:
# $ kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
# new_password_123 (updated within 1 hour, no pod restart)
Output
new_password_123
⚠ Security Trap:
External secrets operators sync into native Secrets, which are still unencrypted at rest unless you've enabled etcd encryption (see previous section). Layer your defenses: encrypt etcd, lock down Secret access with RBAC, and use vault-side IAM so no human touches raw secrets.
🎯 Key Takeaway
Use external secret operators for automatic rotation at scale; never manage secrets by hand in production.
All application pods restarted simultaneously after a Secret update. Pods logged 'authentication failed' errors. Database connection pool exhausted. Customer-facing 500 errors spiked.
Assumption
The new password in the Secret was incorrect or the database had not been updated yet.
Root cause
The database password was injected as an environment variable. Kubernetes does not update environment variables in running pods when a Secret changes — it only updates mounted volume files. The ops team updated the Secret in Kubernetes AND the database simultaneously. However, the pods were not restarted, so they kept using the old password from their environment. When the pods were eventually restarted (either manually or by a deployment trigger), all pods restarted at once because there was no rolling restart strategy, causing a connection storm against the database.
Fix
1. Switched from env-var injection to volume mount for the Secret containing the database password. The kubelet updates mounted Secret files within a configurable sync interval (default ~60 seconds).
2. Implemented application-level graceful credential reload (watching the mounted file for changes) instead of reading env vars once at startup.
3. Added a rolling restart strategy using kubectl rollout restart deployment with maxUnavailable: 1 to avoid simultaneous restarts.
4. Configured the application's connection pool to retry authentication with exponential backoff.
Key lesson
Env vars are a snapshot at pod start time. They do NOT update when the source Secret changes.
Volume-mounted Secrets support live rotation, but the application must be designed to re-read the file.
Never restart all pods simultaneously during credential rotation. Use rolling restarts.
Test your rotation runbook in staging before executing in production.
Production debug guideSymptom-first investigation path for configuration and credential failures in Kubernetes.5 entries
Symptom · 01
Pod fails to start with 'ConfigMap/Secret not found' error.
→
Fix
Verify the ConfigMap/Secret exists in the SAME namespace as the pod. Check for typos in the name. Ensure it was created before the pod was scheduled.
Symptom · 02
Pod starts but application reads stale configuration values.
→
Fix
Check if config is injected as env var (stale) or volume mount (potentially live). If mounted, check kubelet sync interval. If env var, pod restart is required.
Symptom · 03
Secret values visible in kubectl describe pod output.
→
Fix
This is expected for env-var-injected Secrets. The value is base64-decoded and shown in the pod spec. Switch to volume mount to limit exposure. Review RBAC to restrict who can describe pods.
Symptom · 04
Pod gets 'permission denied' when reading a mounted Secret file.
→
Fix
Check the defaultMode field in the volume mount. Verify the pod's securityContext.runAsUser can read the file. Default mode is 0644 but can be overridden.
Symptom · 05
etcd storage growing unexpectedly large.
→
Fix
Check for large ConfigMaps/Secrets (e.g., binary data, large JSON blobs). Use kubectl get secret <name> -o json | wc -c to measure size. Consider external secret stores for large payloads.
★ ConfigMap and Secret Triage CommandsRapid commands to isolate configuration and secret issues.
Pod cannot find a ConfigMap or Secret.−
Immediate action
Verify existence and namespace.
Commands
kubectl get configmap <name> -n <namespace>
kubectl get secret <name> -n <namespace> -o yaml
Fix now
If missing, create it. If in wrong namespace, move the resource or update the pod spec.
Secret value appears wrong or empty.+
Immediate action
Decode and inspect the Secret data.
Commands
kubectl get secret <name> -o jsonpath='{.data.<key>}' | base64 -d
kubectl describe secret <name>
Fix now
If the decoded value is wrong, update the Secret. If empty, check the key name in both the Secret and the pod spec.
Mounted ConfigMap file not updating after ConfigMap change.+
Immediate action
Check kubelet sync delay and mount type.
Commands
kubectl exec <pod> -- cat /path/to/config
kubectl get configmap <name> -o yaml | grep -A 5 data
Fix now
Wait up to 60s for kubelet sync. If still stale, check if the mount uses subPath (subPath mounts do NOT auto-update).
Application logs show 'access denied' to Secret file.+
Immediate action
Check file permissions and security context.
Commands
kubectl exec <pod> -- ls -la /path/to/secret
kubectl get pod <pod> -o jsonpath='{.spec.securityContext}'
Fix now
Set defaultMode: 0440 on the Secret volume. Ensure runAsUser matches the application's expected UID.
ConfigMap vs Secret: Key Differences
Aspect
ConfigMap
Secret
Purpose
Non-sensitive configuration data
Sensitive credentials, keys, tokens
Encoding
Plain text (UTF-8)
Base64-encoded (NOT encrypted by default)
Size Limit
1 MiB per ConfigMap
1 MiB per Secret
Encryption at Rest
Not encrypted by default (can be enabled)
Not encrypted by default (envelope encryption opt-in)
Env Var Injection
Supported (visible in pod spec)
Supported (values visible in kubectl describe pod)
ConfigMaps and Secrets decouple configuration from container images, but they have fundamentally different security profiles.
2
Base64 is not encryption. Enable envelope encryption for Secrets at rest. Use external secret stores for mature environments.
3
Volume mounts support live rotation; env vars do not. Design your application to re-read mounted files.
4
RBAC is the real security boundary. Use dedicated service accounts, restrict to specific Secret names, and disable automounting.
5
Never use subPath for rotating secrets. Never grant list on Secrets unless absolutely required.
6
Test your rotation runbook. The gap between 'Secret updated' and 'pod using new credentials' is where outages happen.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
FAQ · 5 QUESTIONS
Frequently Asked Questions
01
What is the difference between ConfigMaps and Secrets in Kubernetes?
ConfigMaps store non-sensitive configuration data (plain text). Secrets store sensitive data (base64-encoded by default, with optional envelope encryption at rest). Both can be injected as environment variables or mounted as volume files. The key difference is security: Secrets have additional RBAC and encryption considerations.
Was this helpful?
02
Is base64 encoding in Secrets secure?
No. Base64 is a serialization format, not encryption. Anyone with read access to the Secret resource can decode the values. Security comes from RBAC (who can access the Secret) and optionally from envelope encryption (protecting the data at rest in etcd).
Was this helpful?
03
How do I rotate a Secret without downtime?
Mount the Secret as a volume (not an env var). The kubelet will sync the updated file within ~60 seconds. Design your application to re-read the file. For critical rotations, perform a rolling restart of pods after updating the Secret, using kubectl rollout restart deployment with controlled maxUnavailable.
Was this helpful?
04
What is envelope encryption and should I enable it?
Envelope encryption encrypts Secret values before writing to etcd, using a local Data Encryption Key (DEK) which is itself encrypted by a Key Encryption Key (KEK). Yes, enable it in production. Without it, Secrets are stored in plain text in etcd. Use a cloud KMS (AWS KMS, GCP KMS) as the KEK provider for automated key management.
Was this helpful?
05
When should I use an external secret store instead of native Kubernetes Secrets?
Use external stores when you need: centralized secret management across clusters, automatic rotation policies, audit logging for compliance, or secrets shared across cloud accounts. The Secrets Store CSI Driver or External Secrets Operator are the integration layers. The trade-off is operational complexity and a runtime dependency on the external store.