DEV Community

Cover image for Day 19/40 - Kubernetes ConfigMaps and Secrets
Adeoye Malumi
Adeoye Malumi

Posted on

Day 19/40 - Kubernetes ConfigMaps and Secrets

Today's mission: stop hardcoding values into pod manifests and learn how to inject them properly using ConfigMaps and Secrets. I also managed to break a few things along the way, which honestly taught me more than if everything had just worked first try. Let's get into it.


What is a ConfigMap?

As your manifest grows, it becomes harder to manage a pile of environment variables directly inside it. A ConfigMap lets you:

  • Pull those key-value pairs out of the manifest and store them as their own Kubernetes object
  • Inject that ConfigMap into a pod as environment variables (or as files, but that's for another day)
  • Reuse the same ConfigMap across multiple pods, so you're not repeating yourself everywhere

Basically, it's a clean way to separate configuration from your application definition.

Creating a ConfigMap

kubectl create cm app-cm --from-literal=firstname=adeoye --from-literal=lastname=malumi
Enter fullscreen mode Exit fullscreen mode

Here, firstname and lastname are the keys, and adeoye / malumi are the values.

My first "gotcha" of the day

I actually tried to split this command across two lines like this:

kubectl create cm app-cm --from-literal=firstname=adeoye \
--from-literal=lastname=malumi
Enter fullscreen mode Exit fullscreen mode

First attempt gave me:

error: exactly one NAME is required, got 2
Enter fullscreen mode Exit fullscreen mode

Turns out I had a trailing space after the backslash on the first line. In bash, the \ has to be the very last character on the line for line continuation to work. A space after it breaks the continuation, and the shell just treats the next line as a separate (garbled) argument. When I finally got the syntax right, kubectl describe cm app-cm showed the value for firstname as adeoye--from-literal=lastname=malumi — my second flag had literally been glued onto the first value as text!

Lesson learned: either keep the command on one line, or make sure there's truly nothing (not even a space) after your \.

Once fixed, describe showed exactly what I wanted:

Data
====
firstname:
----
adeoye

lastname:
----
malumi
Enter fullscreen mode Exit fullscreen mode

Injecting a ConfigMap into a Pod

Here's the pod manifest I used to consume the ConfigMap as environment variables:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    env:
    - name: FIRSTNAME
      valueFrom:
        configMapKeyRef:
          name: app-cm
          key: firstname
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
Enter fullscreen mode Exit fullscreen mode

The valueFrom.configMapKeyRef block tells Kubernetes: "don't hardcode this value — go fetch it from the app-cm ConfigMap, using the firstname key."


Gotcha #2: Pods are mostly immutable

After creating my ConfigMap with both firstname and lastname, I went back and added a LASTNAME env var to my already-running pod's manifest, then ran:

kubectl apply -f pod.yaml
Enter fullscreen mode Exit fullscreen mode

And got slapped with:

The Pod "myapp-pod" is invalid: spec: Forbidden: pod updates may not change fields other than 
`spec.containers[*].image`,`spec.initContainers[*].image`,`spec.activeDeadlineSeconds`,
`spec.tolerations` (only additions to existing tolerations),
`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)
Enter fullscreen mode Exit fullscreen mode

This was a great reminder: a bare Pod's spec is mostly frozen once it's created. Kubernetes only lets you patch a small allowlist of fields in-place — image, a couple of grace period settings, and tolerations. Environment variables are not on that list.

Two ways around this:

Option 1 — Delete and recreate:

kubectl delete pod myapp-pod
kubectl apply -f pod.yaml
Enter fullscreen mode Exit fullscreen mode

Option 2 — Force replace in one shot:

kubectl replace --force -f pod.yaml
Enter fullscreen mode Exit fullscreen mode

I went with replace --force, and it worked perfectly:

❯ kubectl replace --force -f pod.yaml
pod "myapp-pod" deleted from default namespace
pod/myapp-pod replaced
Enter fullscreen mode Exit fullscreen mode

Verified inside the container:

❯ kubectl exec -it myapp-pod -- sh
/ # echo $FIRSTNAME
adeoye
/ # echo $LASTNAME
malumi
Enter fullscreen mode Exit fullscreen mode

Side note: this is actually one of the reasons Deployments exist and are preferred over bare Pods in real-world use. A Deployment lets you update the pod template (image, env vars, etc.) and kubectl apply will handle the rolling replacement of pods for you — no manual delete/recreate dance required.


Secrets: same idea, but for sensitive data

Secrets work almost identically to ConfigMaps, except they're meant for sensitive values like passwords, tokens, or usernames you don't want sitting in plain text in your manifests.

I followed the official Kubernetes docs on defining container environment variables using Secret data.

Creating a Secret

kubectl create secret generic backend-user --from-literal=backend-username='adeoye-admin'
Enter fullscreen mode Exit fullscreen mode

Using the sample pod from the docs

kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
Enter fullscreen mode Exit fullscreen mode

This pod injects the secret as an environment variable called SECRET_USERNAME.

One more small hiccup

Right after creating the pod, I tried to exec into it immediately:

❯ kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
error: Internal error occurred: unable to upgrade connection: container not found ("envars-test-container")
Enter fullscreen mode Exit fullscreen mode

Checking kubectl get po showed the pod was still ContainerCreating. Classic case of being too impatient — I just needed to wait a few seconds for the container to actually spin up. Once it hit Running:

❯ kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
adeoye-admin
Enter fullscreen mode Exit fullscreen mode

And there it is — the secret successfully injected as an env var.


Key Takeaways

  • ConfigMaps decouple non-sensitive configuration from your pod manifests, and can be reused across multiple pods.
  • Secrets work the same way but are the right home for sensitive values.
  • Both can be injected into a pod's containers via env.valueFrom.configMapKeyRef or secretKeyRef.
  • Watch your shell syntax — a stray space after a line-continuation \ will silently mangle your command.
  • Pods are mostly immutable once created. Only image, tolerations, and a couple of grace-period fields can be patched live. Everything else means delete + recreate, or kubectl replace --force. This is a big part of why Deployments exist.
  • Give newly created pods a moment to leave ContainerCreating before you exec into them.

That's Day 19 done — small errors, but each one taught me something I'll remember longer than if it had just worked on the first try.


Top comments (0)