Day 4 - Kubernetes Without The Tears


So far, we’ve deployed apps, filtered pods like a seasoned K8s professional, and organized our cluster with labels and namespaces. Now it’s time to tackle something that comes up in every real-world app: configuration.
Hardcoding stuff inside containers? Yeah, let’s not. We are developers - when do we every hard code stuff?
Under the Hood
All apps need things like database connection strings, feature flags, environment variables and API keys and passwords (please don’t put these in plain YAML - we’ll see how we can store these).
Kubernetes gives us two tools for this and their names are pretty self-explanatory: Secrets for well, secrets as in sensitive data and ConfigMaps for all other non-sensitive config data.
Let’s see how they work in K8s.
ConfigMaps: Externalized Settings
ConfigMaps, as their name suggests, let you inject configuration values into your pods—things like app settings, URLs, or even other config files.
You can create them from literal values or from files:
kubectl create configmap my-config --from-literal=APP_MODE=production
kubectl create configmap my-config --from-file=config.json
While this is great, writing a config file in terminal is pretty hard core and if you like doing that, all the power to you and much respect, but I am a bit lazier. Therefore, I prefer to create or edit the config files in my favorite editor, such as Visual Studio Code.
Once you’re done with the file, just use --from-file
to load it in.
--from-file
again, or use kubectl apply -f
with a YAML-based ConfigMap definition. I have to admit, it’s not the most elegant update process.If you missed it please read the callout box above. Updating config files is like pulling teeth in K8s. That’s why many folks choose to manage their config declaratively.
Declarative config means defining your resources in YAML files that describe what you want—rather than imperatively telling Kubernetes what to do step by step. You save the file, version control it (Git, FTW), and apply it like so:
kubectl apply -f configmap.yaml
Now you’ve got a repeatable setup, easy rollbacks, and fewer flags to remember (I did tell you that command kubectl apply -f
is one of the most used ones now, didn’t I?)
To see it:
kubectl get configmap my-config -o yaml
You then reference it in your pod spec using envFrom
, env
, or volumeMounts
.
Example:
envFrom:
- configMapRef:
name: my-config
Or specify individual keys:
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: my-config
key: APP_MODE
If this is a bit overwhelming, do not worry. I think it will become much more clearer when we have our hands on it in the lab section.
Secrets: You show me yours and I’ll show you mine
Secrets work like ConfigMaps, but Kubernetes encodes the values in base64. It’s not encryption—it’s just obscured from casual eyes. You still need to be careful.
Let’s say you want to define a secret using a YAML file. Here’s what it looks like in plain text, and Kubernetes will handle the base64 encoding for you when you use kubectl create secret
:
kubectl create secret generic my-secret --from-literal=DB_PASSWORD=supersecret
You can inspect this secret using
kubectl get secret my-secret -o yaml
to see something like:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
DB_PASSWORD: c3VwZXJzZWNyZXQ=
That c3VwZXJzZWNyZXQ=
is just supersecret
in base64. Kubernetes handles this encoding automatically when you pass values with --from-literal
. You don’t need to encode it yourself unless you’re writing the YAML manually.
echo c3VwZXJzZWNyZXQ= | base64 --decode
if you want to see them.So, this works great if you have one or two secrets, but what if you have a lot of secrets? Afterall, we all have more than one secret, don’t we?
Then you have options. You can pas multiple values inline;
kubectl create secret generic nginx-secret \
--from-literal=API_KEY=12345-abcde \
--from-literal=DB_PASSWORD=supersecret \
--from-literal=LOG_LEVEL=debug
Or you can create a file called secrets.env with key-value pairs (yeah, it is a pair this time unlike the labels we have seen on Day 3).
API_KEY=12345-abcde
DB_PASSWORD=supersecret
LOG_LEVEL=debug
and then use
kubectl create secret generic nginx-secret --from-env-file=secrets.env
But hold on - at the beginning of this article there was this big fat warning about not using secrets in YAML files. Yet, now we have them in plain text files? Yeah, not very secure, is it? If someone gets access to your repo or local machine, they can see your secrets. That’s why in production environments, secrets are often stored in secure vaults such as Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault (offered by IBM).
You can integrate these vaults with Kubernetes using external secret operators or CSI drivers. We won’t dive into those just yet, but keep that in mind when you start deploying to real clusters.
For our learning purposes, however, both .env
file or inline works just fine.
Now that we confused ourselves a lot, let’s stop here and see all this in action before we go any further and enter the twilight zone.
Lab: Config and Secrets
Let’s set up a ConfigMap and a Secret, and wire them into a pod. Remember to be in the k8s-labs
folder (or whatever folder you are using to keep our lab files in)
Step 1: Create the ConfigMap
kubectl create configmap nginx-config --from-literal=APP_MODE=staging -n dev-space
Step 2: Create the secret
We’ve got two options here. We either pass the secret(s) inline;
kubectl create secret generic nginx-secret \
--from-literal=API_KEY=12345-abcde \
--from-literal=DB_PASSWORD=supersecret \
--from-literal=LOG_LEVEL=debug
-n dev-space
Or, we create a secrets.env file in the same directory with this content:
API_KEY=12345-abcde
DB_PASSWORD=supersecret
LOG_LEVEL=debug
and then run the following command:
kubectl create secret generic nginx-secret --from-env-file=secrets.env -n dev-space
FYI, I went with the secrets.env
file option.
Step 3: Update Your Deployment YAML
Add this under spec.template.spec.env
:
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: nginx-config
key: APP_MODE
- name: API_KEY
valueFrom:
secretKeyRef:
name: nginx-secret
key: API_KEY
And save this file as nginx-deployment-day4.yaml
. Here is what the full YAML file looks like after the changes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev-space
labels:
app: nginx
env: dev
tier: frontend
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
env: dev
tier: frontend
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: nginx-config
key: APP_MODE
- name: API_KEY
valueFrom:
secretKeyRef:
name: nginx-secret
key: API_KEY
Then re-apply the deployment:
kubectl apply -f nginx-deployment-day4.yaml
Step 4: Check the Environment Inside the Pod
Let’s find the pod name:
kubectl get pods -n dev-space
Then exec into it:
kubectl exec -n dev-space -it <pod-name> -- /bin/sh
Don’t be alarmed if all you see is a lonely #
prompt—that means you’ve successfully dropped into the container shell. It’s a minimal shell and it’s ready for commands.
Let’s give that pound sign a purpose: check the env variables:
env | grep APP_MODE
env | grep API_KEY
Boom. Config from the outside world.
If you want to explore more:
ls /
to see the file systemcat /etc/os-release
to see what distro you’re inexit
to get out of the container
We’re halfway through. Keep going—this plane’s got plenty of altitude left.
But hold on, don’t leave just yet. Grab another cup of coffee, tea or better yet some more water and stick around. I’d like to add a bonus section.
Bonus: GUI Options for K8s
Because the mouse gets lonely and sometimes you just want to click stuff.
We’ve been living in the command line for a while, and that’s great for automation and scripting.
But let’s be honest—not everyone dreams in YAML and unless you are working with Kubernetes day in day out, you won’t remember any of these commands. Sure, you will look for them and use the cheat sheet that is included in this series, but when you are new at something, a GUI is invaluable. Sometimes, you just need color in your life or even you just want to click and right click just because you can.
Fortunately, Kubernetes has some great GUI options if you want to see what’s going on without typing kubectl
every five minutes. I would like to introduce two of these options with a brief introduction:
Kubernetes Dashboard
This is the official web-based UI. We installed it back in Day 1, remember? (I surely don’t). This dashboard helps us;
View and manage pods, deployments, and services
Inspect logs, scale workloads, and see events in real time
Great for getting a quick visual snapshot
So, let’s launch it:
kubectl proxy
This is the only command we will type - well, maybe one more, but that’s it. If you want, you can put this command in a batch file and double click it. Anyway, once you do this, you will get a message like this:
Starting to serve on 127.0.0.1:8001
So, open your favorite browser and go to that address and… more code. You get a list of all the URLs you can go to. Let’s go to this one:
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login
I know it’s a mouthful but you can bookmark it. Once you are there, you will need to enter a token. Enter:
kubectl -n kubernetes-dashboard create token dashboard-admin
Copy the generated token, paste it in your browser where it says Token, and finally you get something more than a black and white screen. Click around and see how you like it.
Lens
If you are thinking, Kubernetes Dashboard is still using the CLI, you are right. For a more complete user experience and GUI, there is an open source tool called Lens. It is a desktop app and autodetects your clusters and you can view your logs, resource usage and YAML manifests.
You can download it from https://k8slens.dev/
Once you install it, this is the experience you get:
Kind of like Visual Studio Code but for Kubernetes. Oh, by the way, the default theme is dark but I used light, so we get a visual different than the command line.
We’ll keep using CLI throughout this series—but if you want to explore or debug something quickly, these GUI options can be a lifesaver.
What’s Next
If you’ve made it this far, give yourself a high five — you’re not just using Kubernetes, you’re speaking its language.
Tomorrow we’ll look at probes and scaling—because what good is a healthy app if you can’t tell it’s healthy or scale it when it gets popular?
See you tomorrow on Day 5 - we’ve got some probes to poke and apps to scale 😎.
Subscribe to my newsletter
Read articles from TJ Gokken directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

TJ Gokken
TJ Gokken
TJ Gokken is an Enterprise AI/ML Integration Engineer with a passion for bridging the gap between technology and practical application. Specializing in .NET frameworks and machine learning, TJ helps software teams operationalize AI to drive innovation and efficiency. With over two decades of experience in programming and technology integration, he is a trusted advisor and thought leader in the AI community