Kubernetes — ConfigMap + Secret

A ConfigMap holding non-sensitive application configuration — including a multi-line file stored as a key — paired with a Secret for database credentials and API keys, using stringData for human-readable values.

🛠 Paste this YAML into the validator to check it instantly.

Open Validator →

Overview

Kubernetes separates configuration from application code through two resource types. A ConfigMap stores arbitrary non-confidential key-value data — environment names, log levels, feature flags, and even the content of entire configuration files. A Secret stores sensitive data such as passwords, tokens, and private keys, and is treated differently by the cluster: it can be encrypted at rest (if configured), is not shown in plain text in kubectl describe output, and can be restricted with RBAC policies.

Both resources can be consumed by Pods in two ways: as environment variables (individually with valueFrom or in bulk with envFrom), or as files mounted into the container filesystem via a volume. This example uses the environment variable pattern in the Deployment manifest. The multi-line app.properties key in the ConfigMap is designed to be mounted as a file.

Apply these resources before your Deployment: kubectl apply -f configmap-secret.yaml. If the Secret does not exist when a Pod starts, the Pod will remain in Pending state with an InvalidRef error until the Secret is created.

Full YAML Copy-paste ready

configmap-secret.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app-config
  namespace: default
data:
  APP_ENV: production
  LOG_LEVEL: info
  API_BASE_URL: https://api.example.com
  app.properties: |
    server.port=3000
    server.timeout=30
    feature.new-ui=true
---
apiVersion: v1
kind: Secret
metadata:
  name: my-app-secrets
  namespace: default
type: Opaque
stringData:
  database-url: postgresql://user:password@db-host:5432/myapp
  jwt-secret: change-me-to-a-random-secret-at-least-32-chars
  api-key: my-secret-api-key

Key sections explained

ConfigMap for non-sensitive configuration

The ConfigMap's data field is a flat map of string keys to string values. Simple values like APP_ENV: production or LOG_LEVEL: info can be injected directly as environment variables in a Deployment. The key name becomes the environment variable name, and the value becomes the environment variable value. Using a ConfigMap instead of hardcoding values in the Deployment manifest means you can update configuration without changing the Deployment — and without triggering a rolling restart, unless you have a mechanism like Reloader watching for ConfigMap changes.

ConfigMaps can also be mounted as volumes, where each key becomes a file in the mounted directory. This is ideal for applications that read configuration from files rather than environment variables — like Nginx, Prometheus, or any JVM application.

The | block scalar for multi-line file content

The app.properties key uses YAML's block scalar indicator | (literal block style) to store multiple lines of text as a single string value. Every indented line after the | becomes part of the value, with newlines preserved. This is a clean way to embed an entire configuration file inside a Kubernetes manifest. When this ConfigMap key is mounted as a volume, the resulting file in the container will contain exactly those three lines, making it indistinguishable from a regular file on disk.

stringData vs data in Secrets

Kubernetes Secrets have two fields for storing values: data and stringData. The data field requires all values to be base64-encoded — which is error-prone and makes the manifest harder to read and review. The stringData field accepts plain-text values and Kubernetes automatically base64-encodes them when storing the Secret in etcd. When you read the Secret back with kubectl get secret my-app-secrets -o yaml, you will see the values under the data field (base64-encoded), not stringData.

You can mix data and stringData in the same Secret — for example, if you need to store a binary certificate (in data) alongside a plain-text password (in stringData). If the same key appears in both, stringData wins.

type: Opaque

Opaque is the generic, unstructured Secret type. It places no constraints on the keys or values — you can store anything. Kubernetes also has built-in types for specific use cases: kubernetes.io/tls for TLS certificates (used automatically by cert-manager), kubernetes.io/dockerconfigjson for image pull credentials, and kubernetes.io/service-account-token for service account tokens. Using the right type gives Kubernetes and tooling additional context, but for general application secrets, Opaque is the correct choice.

Secrets are not encrypted at rest by default

Despite the name, Kubernetes Secrets are stored in etcd base64-encoded but not encrypted by default. Anyone with direct etcd access or broad RBAC permissions can read them. For production clusters, you should enable encryption at rest for Secrets in the API server configuration. A better long-term approach is to use an external secret manager such as HashiCorp Vault, AWS Secrets Manager, or GCP Secret Manager, integrated into Kubernetes via the External Secrets Operator. These tools keep sensitive data out of etcd entirely and provide audit logs, rotation, and fine-grained access control. Never commit a Secret manifest containing real credentials to version control — use placeholder values and override them at deploy time.

Tips & variations

Mount a ConfigMap key as a file

To mount the app.properties key as a file at /etc/my-app/app.properties inside the container, add volumes and volumeMounts to the Deployment:

volume mount snippet
      volumes:
        - name: config-volume
          configMap:
            name: my-app-config
            items:
              - key: app.properties
                path: app.properties
      containers:
        - name: my-app
          volumeMounts:
            - name: config-volume
              mountPath: /etc/my-app

Load all ConfigMap keys as environment variables

Instead of mapping keys one by one with valueFrom.configMapKeyRef, use envFrom to inject all keys from a ConfigMap (or Secret) as environment variables at once:

envFrom snippet
          envFrom:
            - configMapRef:
                name: my-app-config
            - secretRef:
                name: my-app-secrets

Create a Secret from the command line

You can create a Secret without writing a manifest — which keeps plaintext values out of your repo: kubectl create secret generic my-app-secrets --from-literal=database-url='postgresql://...' --from-literal=jwt-secret='...'. Combine this with a CI/CD secret store for automated deployments.