ConfigMaps and Secrets

Keep settings in ConfigMaps and passwords in Secrets, pass them to a container as environment variables or files, and know when a change reaches a running Pod.

40 min 2 hands-on tasksWorkloads · 15% of the exam

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Create a ConfigMap or a Secret from literals, a file or an env file in one command, and decode any Secret value.
  • Pass one key with env.valueFrom, every key with envFrom, and files with a configMap or secret volume, including items and subPath.
  • Say which of these ways picks up a change, and what immutable: true does.

Your app needs settings: a mode, a color, a database password. You should not build them into the image, because then every change needs a new image. Kubernetes keeps them in two objects instead:

  • A holds plain settings, such as APP_MODE=production.
  • A holds sensitive values, such as the password of your Django app's database.

Both are a list of keys with values. A gets them in one of two ways: as environment variables, or as files in a folder.

How settings reach a container

ConfigMap or Secret

keys and values

Two ways in

Environment variable

read once, at start

Mounted file

one file per key

Container

your app reads them

Exam tasks here are short and very literal: a named object with named keys, used in a set way by a set container. The grader reads the object, then the spec. It often runs env or cat inside the container too. Every detail in the task can be checked, so read it twice.

Create them with one command

Each source has its own flag, and the same flags work for ConfigMaps and Secrets.

One command per source type
# ConfigMaps
kubectl -n wl-config create configmap app-config --from-literal=APP_MODE=production --from-literal=APP_COLOR=blue
kubectl -n wl-config create configmap nginx-conf --from-file=default.conf=./site.conf   # key=path sets the key name
kubectl -n wl-config create configmap app-env --from-env-file=./app.env                  # one key per KEY=VALUE line
kubectl -n wl-config create configmap scripts --from-file=./scripts/                      # one key per file in the directory

# Secrets: same flags, plus a type
kubectl -n wl-config create secret generic db --from-literal=username=app --from-literal=password='s3cr3t!'

# Look before you mount
kubectl -n wl-config describe configmap app-config
kubectl -n wl-config get secret db -o yaml
FlagThe key isThe value is
--from-literal=K=VKV
--from-file=pathThe file's name, or the name you give with key=pathThe whole file
--from-env-file=pathOne key for each KEY=VALUE lineThe text after =. Quotes are kept, so A="x" stores the quotes too

Mixing up the last two is the most common reason a file or a variable comes out wrong. Take a Django app's .env file. --from-env-file=.env gives you DEBUG, SECRET_KEY and so on as separate keys. --from-file=.env gives you one key, .env, holding the whole file.

Secrets also have a type. For your own apps it is Opaque, which kubectl create secret generic makes. Other types each have one set job, and each is taught where it is used. TLS certificates are in Ingress, registry logins in Application failures, and tokens in RBAC.

base64 is an encoding, not protection

Secret values are stored in base64. That is an encoding, not encryption: anyone can turn it back into the original text with one command.

Reading and writing Secret values
kubectl -n wl-config get secret db -o jsonpath='{.data.password}' | base64 -d; echo
echo -n 's3cr3t!' | base64        # -n matters: without it the trailing newline is encoded too
kubectl -n wl-config create secret generic db2 --from-literal=password='s3cr3t!' --dry-run=client -o yaml
  • In a Secret's , data takes base64 values.
  • stringData takes plain text. The encodes it when it saves the Secret, and never shows stringData back to you.

Secrets are not encrypted by default. Anyone allowed to create Pods in a can read every Secret there, just by mounting it. So the real protection is : who may read Secrets, and who may create Pods.

As environment variables

There are two ways to turn keys into environment variables:

  • env with valueFrom: one key, and you choose the variable's name.
  • envFrom: every key in the object, each as a variable with the key's name.
Environment variables: one key, or every key
apiVersion: v1
kind: Pod
metadata:
  name: env-demo
  namespace: wl-config
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sleep", "3600"]
      env:
        - name: MODE                  # one key, under a new name
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_MODE
        - name: DB_PASS
          valueFrom:
            secretKeyRef:
              name: db
              key: password
      envFrom:                        # every key becomes a variable
        - configMapRef:
            name: app-config
        - prefix: DB_                 # username becomes DB_username
          secretRef:
            name: db
  • If the ConfigMap, the Secret or the key does not exist, the container does not start. The Pod shows CreateContainerConfigError, and kubectl describe pod names the missing piece.
  • Add optional: true to a reference only when the task allows the value to be missing.
  • If env and envFrom set the same name, the one under env wins.

As files

A ConfigMap or Secret can also be a volume. Each key becomes a file, and the value becomes the file's contents. For example, a ConfigMap key default.conf becomes a file default.conf that nginx reads as its config.

Files: a whole object, selected keys, or one file with subPath
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          volumeMounts:
            - name: conf                 # one file dropped into an existing directory
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: default.conf
            - name: creds                # the whole Secret as a directory of files
              mountPath: /etc/creds
              readOnly: true
      volumes:
        - name: conf
          configMap:
            name: nginx-conf             # configMap uses "name" ...
            items:                       # only these keys, under these file names
              - key: default.conf
                path: default.conf
        - name: creds
          secret:
            secretName: db               # ... secret uses "secretName"
            defaultMode: 0400            # octal in YAML; the API stores it as 256

Watch out: a volume mounted on a folder hides whatever the image had in that folder. That is fine for an empty folder such as /etc/creds. On /etc/nginx it breaks nginx, because its main config file disappears. To add one file to a folder that already has content, use subPath. It has a cost, shown in the next table.

Which changes reach a running Pod

You edit a ConfigMap. Does the running app see the change? It depends on how the container gets it:

Used asSees the change?What to do
env or envFromNo. Variables are read once, when the container starts.Restart: kubectl rollout restart deploy/<name>
A volume (whole object or items)Yes, usually within a minute or two. The app must read the file again.Wait, or restart if the app keeps its config in memory
A volume with subPathNever.Restart the Pod
immutable: trueThe object itself can no longer change.Delete it, create it again, then restart the Pods that use it

immutable: true on a ConfigMap or Secret is a one-way door. You cannot unset it; you can only delete the object. In return, the stops watching the object, which takes load off the API server in big clusters. And no one can change the config under a running app by accident.

In the official docs· open on exam day too

Hands-on task 1 of 2wl-config-1
~7 min5% weightWorkloads

Configure a Deployment from a ConfigMap and a Secret

In namespace wl-config: create a ConfigMap app-config with the keys APP_MODE=production and APP_COLOR=blue, and a Secret app-secret of type Opaque with the key DB_PASSWORD=s3cr3t-pass. Then create a Deployment app with 2 replicas, image busybox:1.36, container name app, running sleep 3600. The container must get an environment variable MODE from the key APP_MODE of app-config, every key of app-secret as an environment variable, and every key of app-config as a file under /etc/app.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/wl-config-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/wl-config-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/wl-config-1/cleanup | bash
Hands-on task 2 of 2wl-config-2
~7 min5% weightWorkloads

Replace a config file and mount a Secret with strict permissions

A replacement nginx server block is in /tmp/cka/wl-config/default.conf. In namespace wl-config, create a ConfigMap proxy-conf from that file with the key default.conf, and a Secret proxy-token with the key token set to abc123. Then create a Pod proxy (image nginx:1.27) in which the ConfigMap's file replaces /etc/nginx/conf.d/default.conf, so that http://localhost:8080/healthz inside the Pod returns healthy, and the Secret is available as the file /etc/proxy/token with permissions 0400.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/wl-config-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/wl-config-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/wl-config-2/cleanup | bash

Finished this lesson?

0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.