What is the difference between ConfigMap and Secret
Both ConfigMap and Secret are native Kubernetes API objects designed to decouple application configuration and credentials from compiled container image binaries (adhering to Fa...
🟢 Junior Level
30-Second Summary
Both ConfigMap and Secret are native Kubernetes API objects designed to decouple application configuration and credentials from compiled container image binaries (adhering to Factor III of the Twelve-Factor App methodology).
The fundamental distinction lies in data confidentiality and sensitivity:
ConfigMap: Designed for non-confidential, plain-text configuration settings (database hostnames, service ports, feature flags, logging levels). Values are stored in raw human-readable text.Secret: Designed for sensitive credentials and keys (database passwords, private TLS/SSH keys, OAuth tokens, Docker registry credentials). In manifests, values are encoded in Base64 format.
Real-World Analogy
- ConfigMap is a sticky note attached to an employee’s monitor: It lists the office cafeteria extension number and conference room names. Anyone walking past can read it without posing an organizational security threat.
- Secret is a locked steel safe beneath the manager’s desk: Inside are company seals, corporate banking keys, and employee personnel files. Access is granted strictly to authorized individuals under formal access controls.
Basic Manifest Examples
1. ConfigMap (Plain Text)
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
DB_HOST: "postgres.prod.svc.cluster.local"
DB_PORT: "5432"
SPRING_PROFILES_ACTIVE: "prod"
2. Secret (Base64 Encoded)
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
type: Opaque
data:
# echo -n "secret123" | base64 -> c2VjcmV0MTIz
DB_PASSWORD: c2VjcmV0MTIz
[!WARNING] Base64 is NOT Encryption! Base64 is merely a bidirectional binary-to-text encoding algorithm. Anyone with RBAC read access to the namespace can decode values instantly with
echo "c2VjcmV0MTIz" | base64 -d.
🟡 Middle Level
Consumption Patterns: Environment Variables vs. Mounted Volumes
| Injection Method | Manifest Declaration | Live Update Behavior |
|---|---|---|
| Environment Variables | env.valueFrom / envFrom |
❌ Static: Frozen at container launch (execve). Requires a Pod restart (kubectl rollout restart) to ingest new values. |
| Mounted Volumes | volumes.configMap / secret |
🟢 Dynamic: Kubelet periodically updates mounted files on disk without restarting the Pod container. |
1. Via Environment Variables (env / envFrom)
containers:
- name: java-app
image: order-service:1.0
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: order-service-config
key: DB_HOST
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: order-service-secret
key: DB_PASSWORD
Limitation: Changing the secret in Kubernetes will never update the environment variable of a currently running Linux process.
2. Via Volume Mounts
containers:
- name: java-app
image: order-service:1.0
volumeMounts:
- name: config-dir
mountPath: /app/config
readOnly: true
volumes:
- name: config-dir
configMap:
name: order-service-config
Advantage: Kubelet continuously observes ConfigMap revisions and updates files on disk automatically without restarting the container.
The stringData Convenience Field in Secret
To eliminate the manual step of running base64 on every credential during YAML drafting, Kubernetes provides the stringData field:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
stringData:
DB_PASSWORD: "plain-text-password-here" # Written in plain text!
Upon saving the manifest, the API Server automatically converts all values in stringData into Base64 format and populates the standard data field.
The immutable: true Guard
Starting with Kubernetes 1.21, both objects support the immutability flag:
immutable: true
- Protection Against Accidental Drift: Prevents unintended configuration modifications from breaking live production workloads.
- Substantial Cluster Performance Gain: In clusters hosting thousands of pods, Kubelet stops polling the API Server for updates to immutable objects, significantly reducing CPU and memory consumption on the control plane.
🔴 Senior Level
The Illusion of Secret Security & Encryption at Rest
By default, Kubernetes persists all Secret objects in the cluster datastore (etcd) as unencrypted plain text (Base64 only). Anyone who obtains an etcd snapshot backup or access to the master node filesystem can extract all cluster credentials.
Implementing Real Encryption at Rest
Encryption at rest must be explicitly configured on kube-apiserver via the --encryption-provider-config parameter:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
# Enterprise recommendation: Cloud KMS hardware envelope encryption
- kms:
apiVersion: v2
name: aws-kms-provider
endpoint: unix:///var/run/kmsplugin/socket.sock
# Alternative: Local symmetric AES-CBC / AES-GCM cipher
- aescbc:
keys:
- name: key1
secret: <32-byte-base64-key>
- identity: {} # Fallback provider to read existing unencrypted records
Atomic Volume File Swapping via Symlinks (Torn Read Prevention)
When Kubelet updates a mounted ConfigMap or Secret volume, it does not rewrite file bytes directly. If a running Java application read the configuration while a multi-byte write was in progress, a corrupt partial state (Torn Read) would occur.
Kubernetes avoids this by maintaining a three-tiered symbolic link hierarchy:
/app/config/
├── application.yaml -> ..data/application.yaml
├── ..data -> ..2026_09_28_14_30_00.123456789 (Symlink pointing to the current directory)
└── ..2026_09_28_14_30_00.123456789/ (Timestamped directory containing real files)
└── application.yaml
When an update occurs:
- Kubelet creates a new timestamped directory containing the updated files.
- Kubelet executes an atomic POSIX filesystem symlink swap (
rename()) pointing..datato the new directory. - Applications reading the file either see the old version or the new version completely, never an inconsistent intermediate state.
Enterprise Production Standard: External Secrets Operator (ESO)
In modern GitOps and production architectures, secrets are never committed to Git repositories or created manually via raw YAML.
Instead, teams deploy the External Secrets Operator (ESO):
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: order-db-secret
spec:
secretStoreRef:
name: hashicorp-vault # Integrates with HashiCorp Vault, AWS Secrets Manager, or GCP Secret Manager
kind: ClusterSecretStore
target:
name: order-service-db-creds # Name of the generated native K8s Secret
data:
- secretKey: password
remoteRef:
key: secret/data/production/database
property: password
ESO continuously synchronizes credentials, orchestrates automated secret rotations, and revokes access without human intervention.
4 Tricky Questions
1. If you update a value in a ConfigMap, do environment variables (env) and mounted volumes (volumeMounts) update automatically inside an already running container?
Answer:
- Environment Variables (
env/envFrom): Never update automatically. Environment variables are injected by the Linux kernel during process creation (execve) and remain immutable throughout the process lifecycle. To apply updated environment variables, the Pod must be restarted (e.g., viakubectl rollout restart deployment/<name>). - Mounted Volumes (
volumeMounts): Update automatically. The Kubelet periodic sync loop (defaulting to between 10 and 60 seconds) detects the change and updates the..datasymlink on the container filesystem. However, the runtime application (e.g., Spring Boot) must explicitly reload the file from disk (via Spring Cloud Kubernetes, Spring Boot Actuator@RefreshScope, or a custom filesystem watcher).
2. What is the difference between data and stringData in a Secret specification, and what happens if the exact same key is specified in both fields simultaneously?
Answer:
data: Accepts values that are strictly Base64-encoded strings. Used primarily in programmatic CI/CD pipelines and automated deployments.stringData: Accepts values in raw plain text. It is a write-only convenience field created to simplify manual manifest creation.- Key Conflict: If the same key is declared in both
dataandstringData, the value instringDatatakes absolute precedence and overwrites the value indata. When retrieving the Secret via the Kubernetes API,stringDatais stripped, and the resolved values are returned exclusively under thedatafield in Base64 encoding.
3. Why does Java’s java.nio.file.WatchService, when registered directly on /app/config/application.properties, permanently stop receiving file modification events after the first ConfigMap update?
Answer:
Kubelet updates mounted volume files by creating a new timestamped directory and atomically re-linking the ..data symlink, rather than writing bytes into the existing file inode.
When WatchService is attached directly to the specific file /app/config/application.properties, it tracks the underlying Linux inode. During the atomic swap, the symlink changes to point to a new directory and new inode, leaving the previously registered inode orphaned. The operating system never emits subsequent ENTRY_MODIFY events for the old inode.
Solution: Register WatchService to monitor the parent directory (/app/config/), listen for ENTRY_CREATE / ENTRY_MODIFY events on the directory level, and re-open the file stream when the directory link changes.
4. What is the maximum data capacity limit for an individual ConfigMap or Secret object, and what architectural constraint enforces this?
Answer:
The maximum size for any individual ConfigMap or Secret object is strictly 1 Megabyte (1 MiB).
This limit is enforced by the underlying cluster datastore, etcd:
- By default,
etcdenforces a strict 1.5 MiB request and transaction limit on all gRPC payloads. - Storing oversized files (e.g., application JARs, heavy datasets, or extensive certificates) causes memory bloat, high Raft consensus latency, and database fragmentation across control plane nodes. Large datasets must be stored in persistent storage (
PersistentVolumeClaim) or external object storage (AWS S3, MinIO).
🎯 Interview Cheat Sheet
Core Concepts at a Glance
- ConfigMap: Plain-text configuration (URLs, ports, log levels); visible in cleartext.
- Secret: Confidential data (passwords, tokens, certificates); Base64-encoded in YAML.
- Base64 is NOT Encryption: For true security in
etcd, enable KMS Encryption at Rest. - Size Limit: Hard ceiling of 1 MiB per object (enforced by
etcdtransaction limits). - Environment Variables: Static snapshot at boot; requires Pod restart to reflect changes.
- Mounted Volumes: Dynamic file updates via Kubelet symlink swapping (
..data). stringData: Write-only plain-text field; auto-converted to Base64 by API Server.immutable: true: Protects against drift and significantly reduces Kubelet watch API traffic.- Production Standard: External Secrets Operator (ESO) + HashiCorp Vault / Cloud KMS.
Comparison Table
| Feature | ConfigMap | Secret |
| :— | :— | :— |
| Manifest Format | UTF-8 Plain Text | Base64 (data) or Plain Text (stringData) |
| Storage in etcd | Unencrypted Plain Text | Plain Text (default) / Encrypted (KMS/AES) |
| Standard Types | Generic / None | Opaque, kubernetes.io/tls, dockerconfigjson |
| Primary Use Cases | App properties, feature flags, DB URLs | Passwords, TLS keys, API tokens |
| Storage Limit | 1 MiB | 1 MiB |
Red Flags (What NOT to Say)
- ❌ “Kubernetes Secrets provide built-in security because Base64 encrypts credentials.” — Base64 is reversible encoding, not encryption.
- ❌ “Updating a ConfigMap will automatically update environment variables in running Java containers.” — Linux process environment variables never update without process recreation.
- ❌ “You can store a 50MB binary archive or model file in a ConfigMap.” — Any object over 1 MiB will be rejected by the API Server / etcd.
Related Topics
- What is Kubernetes and Why is It Needed — Control plane and etcd architecture
- What is Pod in Kubernetes — Workload environment and volume mounts
- What is Ingress in Kubernetes — TLS termination using
kubernetes.io/tlsSecrets - How to Organize Rolling Update in Kubernetes — Propagating config changes via rollouts