Section 4 · Kubernetes Core
ConfigMaps, Secrets and Namespaces
20 min3 min readFill in the blanks
Lesson at a glance
Move notes-api’s settings into a ConfigMap and its credentials into a Secret, put everything in a notes namespace, and run a reviewable apply-and-diff workflow from the k8s folder.
- 1A namespace for notes-api
- 2ConfigMaps for settings
- 3Secrets for credentials
- 4Wiring them into notes-api
- 5The everyday workflow
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Right now the notes-api Deployment has PORT written into it, the database Deployment has its password in plain text, and everything lives in the default namespace alongside whatever else people try in this cluster. That’s three ways to cause an incident: change a setting and you must edit the workload; read the repository and you have the password; run a careless kubectl delete in default and you hit someone else’s work.
A namespace for notes-api
A namespace is a scope for names. Two teams can both have a Deployment called api in different namespaces. Namespaces are also where you attach permissions (RBAC) and resource quotas, which makes them the natural unit for “one app” or “one team”.
apiVersion: v1
kind: Namespace
metadata:
name: notesAdd namespace: notes to the metadata of every other object in k8s/, then make it your default for this context so you don’t type -n notes every time:
kubectl apply -f k8s/namespace.yaml
kubectl config set-context --current --namespace=notesWhat namespaces don’t do is isolate the network. A Pod in notes can connect to a Service in any other namespace unless a NetworkPolicy says otherwise, and your cluster’s network plugin enforces it. Don’t treat a namespace as a security wall on its own.
ConfigMaps for settings
A ConfigMap holds non-sensitive key-value configuration:
apiVersion: v1
kind: ConfigMap
metadata:
name: notes-api-config
namespace: notes
data:
PORT: "3000"
LOG_LEVEL: infoValues are always strings, which is why "3000" is quoted. Now the same notes-api image can run with info logging in production and debug in staging, by applying a different ConfigMap rather than building a different image.
Secrets for credentials
A Secret has the same shape as a ConfigMap but is meant for sensitive values. Create it from the command line (or from your secret manager) rather than from a committed file:
kubectl create secret generic notes-db \
--from-literal=POSTGRES_PASSWORD='dev-only-change-me' \
--from-literal=DATABASE_URL='postgres://notes:dev-only-change-me@notes-db:5432/notes'Here’s the uncomfortable part. Look at what’s stored:
kubectl get secret notes-db -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -dThe value comes straight back. Secret data is base64-encoded, not encrypted. What makes Secrets safer than ConfigMaps is everything around them: RBAC can grant read access to ConfigMaps but not Secrets, the cluster can encrypt Secrets at rest in etcd (managed providers usually do), and kubelets only receive Secrets for Pods scheduled on their node.
Wiring them into notes-api
Update the container in k8s/deployment.yaml:
containers:
- name: api
image: ghcr.io/skylane/notes-api:1.4.0
ports:
- name: http
containerPort: 3000
envFrom:
- configMapRef:
name: notes-api-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: notes-db
key: DATABASE_URLenvFrom turns every key in the ConfigMap into an environment variable. secretKeyRef pulls one key from the Secret. In k8s/db.yaml, replace the literal POSTGRES_PASSWORD value with a secretKeyRef to the same Secret.
If a referenced key doesn’t exist, the container never starts. The Pod shows CreateContainerConfigError, and kubectl describe pod names the missing key.
Secrets and ConfigMaps can also be mounted as files through a volume. Mounted files are updated in running Pods after a short delay; environment variables are fixed at container start. After changing env-based config, roll the Pods:
kubectl rollout restart deployment/notes-apiThe everyday workflow
Your k8s/ folder now holds namespace.yaml, configmap.yaml, db.yaml, deployment.yaml and service.yaml. The loop for every change is the same:
kubectl diff -f k8s/
kubectl apply -f k8s/
kubectl get all -l app.kubernetes.io/name=notes-apidiff is your review step: it shows what will change on the live cluster, field by field, before you change it. Make it a habit and you’ll catch the stray edit that would have reset someone’s emergency fix. When you need different values per environment, Kustomize (built into kubectl as kubectl apply -k) layers small overlays on top of these base files; Helm, in lesson 5.3, is the other common answer.
notes-api now runs on Kubernetes with proper networking and configuration. It’s not production-ready yet: Kubernetes has no idea whether it’s healthy, how much CPU it needs, or how to update it safely. Section 5 covers all three.
Key takeaways
- ConfigMaps hold non-sensitive settings; Secrets hold credentials and are injected as environment variables or mounted files.
- Secret values are only base64-encoded, not encrypted; protect them with RBAC, encryption at rest, and by keeping them out of Git.
- Environment variables from a ConfigMap or Secret are read once at container start, so restart the Deployment after changing them.
- Namespaces scope names, permissions and quotas for a team or app, but they don’t isolate network traffic on their own.
kubectl diff -f k8s/beforekubectl apply -f k8s/shows exactly what will change, like a pull request for the cluster.