Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Read by thought-leaders and decision-makers around the world. Phone Number: +1-650-246-9381 Email: pub@towardsai.net
228 Park Avenue South New York, NY 10003 United States
Website: Publisher: https://towardsai.net/#publisher Diversity Policy: https://towardsai.net/about Ethics Policy: https://towardsai.net/about Masthead: https://towardsai.net/about
Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Founders: Roberto Iriondo, , Job Title: Co-founder and Advisor Works for: Towards AI, Inc. Follow Roberto: X, LinkedIn, GitHub, Google Scholar, Towards AI Profile, Medium, ML@CMU, FreeCodeCamp, Crunchbase, Bloomberg, Roberto Iriondo, Generative AI Lab, Generative AI Lab VeloxTrend Ultrarix Capital Partners Denis Piffaretti, Job Title: Co-founder Works for: Towards AI, Inc. Louie Peters, Job Title: Co-founder Works for: Towards AI, Inc. Louis-François Bouchard, Job Title: Co-founder Works for: Towards AI, Inc. Cover:
Towards AI Cover
Logo:
Towards AI Logo
Areas Served: Worldwide Alternate Name: Towards AI, Inc. Alternate Name: Towards AI Co. Alternate Name: towards ai Alternate Name: towardsai Alternate Name: towards.ai Alternate Name: tai Alternate Name: toward ai Alternate Name: toward.ai Alternate Name: Towards AI, Inc. Alternate Name: towardsai.net Alternate Name: pub.towardsai.net
5 stars – based on 497 reviews

Frequently Used, Contextual References

TODO: Remember to copy unique IDs whenever it needs used. i.e., URL: 304b2e42315e

Resources

Free: 6-day Agentic AI Engineering Email Guide.
Learnings from Towards AI's hands-on work with real clients.
Stop Hardcoding Secrets: A Beginner’s Guide to ConfigMaps and Secrets
Latest   Machine Learning

Stop Hardcoding Secrets: A Beginner’s Guide to ConfigMaps and Secrets

Author(s): Swapnil Ahire

Originally published on Towards AI.

Stop Hardcoding Secrets: A Beginner’s Guide to ConfigMaps and Secrets

Stop Hardcoding Secrets: A Beginner’s Guide to ConfigMaps and Secrets
Kubernetes ConfigMaps and Secrets Explained — A Beginner’s Guide

Hardcoding passwords and config values into container images is a common beginner mistake with real consequences. Here’s how Kubernetes ConfigMaps and Secrets solve it — with hands-on examples for environment variables and mounted config files.

Somewhere on GitHub right now, there’s a public repository with a database password sitting in plain text inside a Dockerfile, committed by someone who was absolutely going to remove it before pushing, and then didn’t. This isn’t a rare, careless-developer story — it’s an extremely common beginner habit, and it happens because early on, hardcoding a value directly into your app feels like the simplest possible solution. It works immediately. It’s one less thing to think about. And it’s a genuinely bad idea for reasons that aren’t obvious until you’ve been burned by them once.

Kubernetes has a clean answer to this problem, split across two related but distinct tools: ConfigMaps and Secrets. Understanding the difference between them — and actually using them instead of baking values into your container image — is one of those habits that quietly separates people still learning Kubernetes from people who’ve actually run something in production.

Let’s fix the habit properly.

A container image should be identical whether it’s running in development, staging, or production. The moment it isn’t, you’ve hardcoded something you shouldn’t have.

Why Hardcoding Configuration Is a Real Problem, Not Just a Style Preference

It’s worth being specific about why this matters, because “it’s best practice” isn’t a very convincing reason on its own.

First, portability breaks. If your database URL is baked directly into your application code or container image, that same image can’t move cleanly between environments — you’d need a separate image for development, staging, and production, which defeats a huge part of what containers are supposed to give you in the first place: build once, run anywhere.

Second, and more seriously — security. A container image is something that gets pushed to a registry, pulled by CI/CD pipelines, potentially shared across teams, and sometimes accidentally made public. Anything embedded inside it should be treated as effectively exposed, eventually, to more people than you intended. A password baked into an image doesn’t stay secret. It just stays undiscovered, for now.

Third — and this one’s easy to underestimate — changing a config value shouldn’t require rebuilding and redeploying your entire application. If your API endpoint changes, or a feature flag needs flipping, or a password gets rotated, you want to update that value without touching the actual application code or triggering a full image rebuild.

Key takeaway: Hardcoded configuration isn’t just messy — it breaks portability, creates real security exposure, and turns simple config changes into unnecessary redeployments.

ConfigMaps: For Anything That Isn’t Sensitive

A ConfigMap is a Kubernetes object that stores non-sensitive configuration data as key-value pairs, separately from your application code and container image. Think application settings, feature flags, API endpoint URLs, log levels — anything your app needs to know at runtime that isn’t secret information.

Here’s a simple ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: "production"
LOG_LEVEL: "info"
API_ENDPOINT: "https://api.example.com"

That’s it — a named collection of settings, sitting in Kubernetes, completely separate from your Docker image. Your image doesn’t know or care what environment it will eventually run in. The ConfigMap decides that at deployment time.

Key takeaway: A ConfigMap is where non-sensitive, environment-specific settings live — the goal is that your container image never needs to change just because a config value does.

Secrets: For Anything That Is

A Secret looks and behaves almost identically to a ConfigMap structurally, but it exists specifically for sensitive values — passwords, API keys, tokens, TLS certificates, anything you genuinely don’t want sitting around in plain text.

apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM=

That value isn’t plain text — it’s Base64-encoded. And here’s an important, commonly misunderstood point worth being upfront about: Base64 encoding is not encryption. It’s trivially reversible by anyone with access to it — it’s an encoding format, not a security mechanism. Kubernetes Secrets provide a meaningful improvement over hardcoding by keeping sensitive values out of your image and application code, and by controlling access through Kubernetes’s role-based access control (RBAC). But out of the box, Secrets aren’t automatically encrypted at rest inside the cluster’s storage layer either, unless you’ve specifically configured encryption at rest, which most production environments should genuinely set up.

Become a Medium member

For real production sensitivity — proper encryption, secret rotation, tighter audit trails — many teams pair Kubernetes Secrets with a dedicated external secrets manager (like HashiCorp Vault, AWS Secrets Manager, or similar tools) rather than relying on native Secrets alone. That’s a more advanced setup worth knowing exists, even if it’s beyond what a beginner needs on day one.

A Secret keeps a password out of your Dockerfile. It doesn’t automatically make that password uncrackable. Those are two different problems, and conflating them is exactly how “we used Secrets” becomes false confidence.

Key takeaway: Secrets are the right home for sensitive values instead of ConfigMaps or hardcoded strings — but treat native Kubernetes Secrets as “better than hardcoding,” not as “fully solved security,” especially for anything genuinely high-stakes.

Getting Values Into Your Pods: Two Real Ways

Having a ConfigMap or Secret sitting in your cluster doesn’t do anything by itself — your Pod needs to actually consume it. There are two common patterns, and knowing when to reach for each one matters.

Method 1: Environment Variables

This is the more common approach for individual config values your application reads at startup — most languages and frameworks already know how to read environment variables, so this usually requires zero application code changes.

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-first-app
spec:
replicas: 3
selector:
matchLabels:
app: my-first-app
template:
metadata:
labels:
app: my-first-app
spec:
containers:
- name: nginx
image: nginx:latest
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD

Notice the pattern: configMapKeyRef pulls a value from the ConfigMap, and secretKeyRef pulls one from the Secret, but both land inside the container as ordinary environment variables your app reads exactly the way it always has. The application code doesn't need to know or care that Kubernetes is involved at all.

Method 2: Mounted Files

Some applications don’t expect configuration as environment variables — they expect an actual config file on disk (think an nginx.conf, an application.properties file, or a JSON config file). For these, you can mount a ConfigMap or Secret directly into the container's filesystem as a file, or a folder of files.

 containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config

With this in place, every key in app-config shows up as an individual file inside /etc/config in the running container — /etc/config/APP_ENV, /etc/config/LOG_LEVEL, and so on, each file containing that key's value. The same pattern works for Secrets by swapping configMap for secret in the volumes section.

Key takeaway: Use environment variables when your app already reads config that way — use mounted files when your app expects an actual config file on disk. Both pull from the same underlying ConfigMap or Secret object.

Creating Secrets Without Hand-Writing Base64

Manually Base64-encoding values and pasting them into YAML is tedious and error-prone, and thankfully unnecessary for everyday use. kubectl can create a Secret directly from the command line:

kubectl create secret generic app-secret \
--from-literal=DB_PASSWORD=password123

This creates the exact same Secret object as the YAML version above, without you manually encoding anything. It’s a small convenience, but it’s also a safer default habit — fewer chances to accidentally leave a decoded password sitting in a YAML file that might end up committed to version control by mistake.

Key takeaway: kubectl create secret is the safer everyday path — it keeps you from manually handling encoded values in files that could easily end up somewhere they shouldn't.

Trying It Yourself

If you still have my-first-app running from earlier in this series, try this end to end: create a ConfigMap and a Secret, wire both into your Deployment as environment variables using the YAML pattern above, apply it, and then check that it worked:

kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml

kubectl exec -it <pod-name> -- printenv | grep -E "APP_ENV|DB_PASSWORD"

Seeing your config values show up inside a running container — without a single one of them having ever touched your Dockerfile — is a small moment, but it’s the exact shift in habit this article is actually about.

The Real Habit Worth Building

None of this is complicated once it’s written down, which is exactly why it’s such an easy thing to skip when you’re moving fast early in a project. The habit worth building isn’t “remember to use ConfigMaps and Secrets.” It’s a slightly different, more useful question to ask yourself every time you’re about to type a config value directly into code or a Dockerfile: if this value needs to change, does that require me to rebuild and redeploy my entire application?

If the answer is yes, that value probably belongs in a ConfigMap or a Secret instead — not because a tutorial said so, but because you’ve just described exactly the problem those two objects exist to solve.

What’s the closest call you’ve had — a secret that almost made it somewhere it shouldn’t have, or one that actually did? What changed after that?

Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.

Published via Towards AI


Towards AI Academy

We Build Enterprise-Grade AI. We'll Teach You to Master It Too.

15 engineers. 100,000+ students. Towards AI Academy teaches what actually survives production.

Start free — no commitment:

6-Day Agentic AI Engineering Email Guide — one practical lesson per day

Agents Architecture Cheatsheet — 3 years of architecture decisions in 6 pages

Our courses:

AI Engineering Certification — 90+ lessons from project selection to deployed product. The most comprehensive practical LLM course out there.

Agent Engineering Course — Hands on with production agent architectures, memory, routing, and eval frameworks — built from real enterprise engagements.

AI for Work — Understand, evaluate, and apply AI for complex work tasks.

Note: Article content contains the views of the contributing authors and not Towards AI.