Managing your app configurations and secrets securely is non-negotiable in modern cloud-native development. Kubernetes offers two native resources to do this:
- 🛠 ConfigMaps for non-sensitive configuration
- 🔐 Secrets for credentials, tokens, and other sensitive values
In this guide, we’ll show how to use both in a real-world Node.js app deployed to Kubernetes.
✨ What You'll Learn
✅ Difference between ConfigMaps and Secrets
✅ Creating secure configurations using YAML
✅ Deploying a Node.js app with injected env vars
✅ Debugging and accessing environment inside the container
✅ Docker + Kubernetes best practices for configuration
📦 Demo Image & Source Code
We're using a public Docker image that reads environment variables and returns them:
- 📦 Docker:
zaheetdeveloper/k8s-config-demo - 🧪 Code + YAMLs: GitHub Repo
📁 Project Structure
k8s-config-demo/
├── index.js
├── Dockerfile
├── configmap.yaml
├── secret.yaml
└── deployment.yaml
🧠 Why ConfigMaps & Secrets?
ConfigMap
- Stores non-sensitive values like
APP_MODE, URLs, etc.
Secret
- Stores sensitive data like
DB_USER,DB_PASSWORD - Encoded in Base64 and supports encryption at rest
Using them ensures:
- 🔐 You don’t hardcode values
- 🔁 You can change config without rebuilding the image
- 🧩 They integrate cleanly into deployments
🧰 The Application Code (index.js)
const http = require('http');
const PORT = process.env.PORT || 3000;
const DB_USER = process.env.DB_USER;
const DB_PASSWORD = process.env.DB_PASSWORD;
const APP_MODE = process.env.APP_MODE || 'dev';
http.createServer((req, res) => {
res.end(`Mode: ${APP_MODE}, DB_USER: ${DB_USER}`);
}).listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
🐳 Dockerfile
FROM node:alpine
WORKDIR /app
COPY index.js .
CMD ["node", "index.js"]
Build and push (already done):
docker build -t zaheetdeveloper/k8s-config-demo:v1 .
docker push zaheetdeveloper/k8s-config-demo:v1
🔧 Step-by-Step Kubernetes Setup
1️⃣ Create the ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_MODE: production
kubectl apply -f configmap.yaml
2️⃣ Create the Secret
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
DB_USER: YWRtaW4= # 'admin'
DB_PASSWORD: c2VjdXJl # 'secure'
kubectl apply -f secret.yaml
3️⃣ Create the Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-config-test
spec:
replicas: 1
selector:
matchLabels:
app: config-app
template:
metadata:
labels:
app: config-app
spec:
automountServiceAccountToken: false
containers:
- name: app-container
image: zaheetdeveloper/k8s-config-demo:v1
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: app-config
key: APP_MODE
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret
key: DB_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORD
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
ephemeral-storage: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
ephemeral-storage: "512Mi"
kubectl apply -f deployment.yaml
🧪 Testing & Debugging
kubectl get pods
kubectl logs -l app=config-app
Expected output:
Server running on port 3000
kubectl port-forward deployment/app-config-test 3000:3000
Visit in browser: http://localhost:3000

kubectl exec -it pod/<pod-name> -- /bin/sh
env
🛡️ Best Practices
- Encrypt secrets at rest using KMS
- Avoid storing secrets in source code
- Use RBAC to control access
- Explore tools like Sealed Secrets or External Secrets Operator
💬 Conclusion
You now know how to:
- Use ConfigMaps and Secrets in Kubernetes
- Inject them securely into containers
- Deploy and inspect a working environment
➡️ Demo image: Docker Hub
📂 Source code & YAMLs: GitHub





