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
Top comments (0)