ConfigMap vs Secret

Key Differences in Kubernetes

ConfigMaps store non-sensitive configuration data like feature flags, config files, and environment variables in plain text. Secrets store sensitive data like passwords, tokens, and TLS certificates with base64 encoding and additional access controls. Both inject configuration into Pods, but Secrets provide extra safeguards for sensitive information.

Side-by-Side Comparison

DimensionConfigMapSecret
Data SensitivityNon-sensitive configuration dataSensitive data — passwords, tokens, keys, certificates
EncodingStored in plain textStored as base64-encoded values (not encrypted by default)
Encryption at RestNot encrypted in etcd by defaultCan be encrypted at rest in etcd via EncryptionConfiguration
Size Limit1 MiB per ConfigMap1 MiB per Secret
RBACStandard RBAC — often broadly accessibleShould have tighter RBAC — restrict who can read Secrets
MountingMounted as files or injected as environment variablesMounted as files or injected as environment variables; files get 0644 permissions by default
kubectl OutputValues shown in plain text with kubectl get -o yamlValues shown as base64 in kubectl get -o yaml
Built-in TypesSingle generic type (Opaque)Multiple types: Opaque, kubernetes.io/tls, kubernetes.io/dockerconfigjson, etc.

Detailed Breakdown

Creating ConfigMaps and Secrets

# ConfigMap — plain text configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  config.yaml: |
    server:
      port: 8080
      timeout: 30s
    features:
      caching: true
# Secret — sensitive data (values are base64 encoded)
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=
  API_KEY: c2VjcmV0LWtleS12YWx1ZQ==

You can also create Secrets with stringData to avoid base64 encoding yourself — Kubernetes encodes it for you:

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  DB_PASSWORD: "password123"
  API_KEY: "secret-key-value"

Injecting Into Pods

Both ConfigMaps and Secrets can be consumed as environment variables or mounted as files.

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: my-app:1.0.0
      env:
        # From ConfigMap
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: LOG_LEVEL
        # From Secret
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: DB_PASSWORD
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: app-config
    - name: secret-volume
      secret:
        secretName: app-secrets

When mounted as volumes, each key becomes a file. The ConfigMap file at /etc/config/config.yaml contains the YAML content. The Secret files at /etc/secrets/DB_PASSWORD contain the decoded value.

Base64 Is Not Encryption

A critical point for interviews: base64 encoding is not a security mechanism. Anyone with access to the Secret resource can decode the values trivially:

echo "cGFzc3dvcmQxMjM=" | base64 -d
# Output: password123

Base64 exists so that Secrets can hold binary data (like TLS certificates) in a JSON/YAML format. It does not protect the data.

Encryption at Rest

By default, Secrets are stored as plain base64 in etcd — anyone with etcd access can read them. To actually encrypt Secrets in etcd, you configure an EncryptionConfiguration on the API server:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-key>
      - identity: {}

This encrypts Secret data before it is written to etcd. ConfigMaps do not typically get this treatment, which is another reason to use Secrets for sensitive data — the ecosystem is designed to add layers of protection around them.

RBAC Considerations

In production, you should restrict Secret access with RBAC:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get"]
    resourceNames: ["app-secrets"]

This grants read access to only the specific Secret named app-secrets. ConfigMaps are often less restricted because they do not contain sensitive data.

Secret Types

Kubernetes has built-in Secret types that add validation:

# TLS Secret
apiVersion: v1
kind: Secret
metadata:
  name: tls-cert
type: kubernetes.io/tls
data:
  tls.crt: <base64-cert>
  tls.key: <base64-key>
# Docker registry credentials
apiVersion: v1
kind: Secret
metadata:
  name: registry-creds
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-docker-config>

ConfigMaps have no type system — they are all generic key-value stores.

Automatic Updates

When a ConfigMap or Secret is mounted as a volume, Kubernetes periodically syncs the file content (the default kubelet sync period is about 60 seconds). This means you can update a ConfigMap and the mounted files will eventually reflect the change without restarting the Pod.

However, environment variables are set at Pod startup and never updated. If you inject a ConfigMap or Secret value as an env var, you must restart the Pod to pick up changes.

External Secret Management

For production environments, many teams avoid storing sensitive values in Kubernetes Secrets directly. Instead, they use external secret managers:

  • Secrets Store CSI Driver — mounts secrets from HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault as files in Pods
  • External Secrets Operator — syncs secrets from external providers into Kubernetes Secret objects
  • Sealed Secrets — encrypts Secrets so they can be safely committed to Git

These tools treat Kubernetes Secrets as a delivery mechanism while keeping the source of truth in a dedicated secrets manager with audit logging, rotation, and fine-grained access control.

Use ConfigMap when...

  • Storing application configuration files (nginx.conf, application.yml)
  • Setting non-sensitive environment variables like LOG_LEVEL or FEATURE_FLAGS
  • Sharing configuration across multiple Pods in the same namespace
  • Providing command-line arguments or config files to containers

Use Secret when...

  • Storing database passwords and connection strings
  • Managing TLS certificates and private keys
  • Storing Docker registry credentials for image pulls
  • Managing API tokens and OAuth client secrets
  • Any data that should not appear in logs or version control

Model Interview Answer

ConfigMaps and Secrets both inject configuration into Pods via environment variables or volume mounts. The key difference is intent and safeguards. ConfigMaps are for non-sensitive data like config files and feature flags, stored in plain text. Secrets are for sensitive data like passwords and TLS certificates. Secrets are base64-encoded in the API and can be encrypted at rest in etcd with EncryptionConfiguration. They also support tighter RBAC policies and are not printed in kubectl describe output. However, base64 is not encryption — you should enable encryption at rest and consider external secret managers like HashiCorp Vault or the Secrets Store CSI driver for production environments.

Related Comparisons