🐳 Section 14 · Question #16

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
  1. Protection Against Accidental Drift: Prevents unintended configuration modifications from breaking live production workloads.
  2. 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

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:

  1. Kubelet creates a new timestamped directory containing the updated files.
  2. Kubelet executes an atomic POSIX filesystem symlink swap (rename()) pointing ..data to the new directory.
  3. 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., via kubectl 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 ..data symlink 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 data and stringData, the value in stringData takes absolute precedence and overwrites the value in data. When retrieving the Secret via the Kubernetes API, stringData is stripped, and the resolved values are returned exclusively under the data field 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, etcd enforces 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 etcd transaction 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.