What are ConfigMaps and Secrets in Kubernetes?

Introduction:
When running applications in Kubernetes, you’ll quickly realize that application configuration and secrets management are just as important as the code itself. Hardcoding configs or credentials into containers is a recipe for disaster.
This is where ConfigMaps and Secrets come in. ConfigMaps handle general application configuration, while Secrets securely store sensitive information like passwords, API tokens, or TLS certificates.
In this guide, we’ll cover both, show you how to deploy them in Azure VMs running Kubernetes, and walk through real examples with Nginx and MySQL.
What is a ConfigMap?
A ConfigMap is a key-value store in Kubernetes used to separate configuration data from application code. This makes it easy to update configurations without rebuilding your containers.
You can:
Store configuration files (.yaml, .json, .ini)
Inject environment variables into pods
Mount config data as files into containers
Example Use Cases for ConfigMaps
Storing an Nginx index.html file
Providing environment variables for staging vs. production
Injecting JSON configs for microservices
What is a Secret?
Secrets are like ConfigMaps, but with security in mind. They store sensitive data that your application needs to run safely.
🔑 Examples include:
Database usernames and passwords
API keys for third-party integrations
TLS certificates for HTTPS
Secrets are base64 encoded (not fully encrypted by default), so they’re better than plain-text configs but should be paired with secure backends like Azure Key Vault.
Azure VM Setup for Kubernetes:
Before we create ConfigMaps and Secrets, let’s set up Kubernetes on an Azure VM.
Step 1 - Create a Resource Group and VM in Azure
Create resource group
az group create --name k8s-rg --location east-us
Create VM (Ubuntu LTS)
az vm create
--resource-group k8s-rg
--name k8s-vm
--image Ubuntu2204
--admin-username azureuser
--generate-ssh-keys
Step 2 - SSH into the VM
ssh azureuser@
Step 3 - Install Kubernetes and kubectl
Update system
sudo apt update && sudo apt upgrade -y
Install Docker
sudo apt install -y docker.io
Install kubeadm, kubelet, kubectl
sudo apt install -y apt-transport-https ca-certificates
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg
https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg]
https://apt.kubernetes.io/ kubernetes-xenial main" |
sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y
kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
Step 4 - Initialize Cluster
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
Set up kube config for current user
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf
$HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Deploy Flannel CNI
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Verify setup:
kubectl get nodes kubectl get pods -A
🌐 Why Deploy Flannel CNI in Kubernetes?
When you initialize a Kubernetes cluster using kubeadm init
, you’ll notice that the cluster starts, but pods cannot communicate with each other across nodes. That’s because Kubernetes itself doesn’t implement networking — it relies on a Container Network Interface (CNI) plugin.
By default:
Nodes are ready after
kubeadm init
,But pods are stuck in
Pending
state until a CNI network plugin is installed,Because each pod needs an IP address inside the cluster network.
This is where Flannel comes in.
🧩 What is Flannel CNI?
Flannel is one of the most widely used CNI plugins that provides a pod network overlay in Kubernetes.
It assigns unique IP addresses to every pod in the cluster.
Ensures pods can communicate across nodes (not just within the same node).
Uses an overlay network (by default VXLAN) to connect pods across machines.
⚡ Why Flannel in This Setup?
Simplicity → Flannel is lightweight and super easy to deploy.
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Compatibility → Works seamlessly with
kubeadm
clusters.Other CNIs (like Calico, Cilium, Weave) offer advanced features (network policies, BPF, encryption),
But for a quick Azure VM Kubernetes lab/demo, Flannel is the simplest to get pods talking to each other.
Pod Networking → Without Flannel,
kubectl get pods
will show pods stuck inPending
because the kubelet can’t assign pod IPs.- After Flannel, each pod gets an IP from the 10.244.0.0/16 CIDR (which is why we passed
--pod-network-cidr=10.244.0.0/16
duringkubeadm init
).
- After Flannel, each pod gets an IP from the 10.244.0.0/16 CIDR (which is why we passed
Azure VM Context →
In AKS (Azure Kubernetes Service), networking is handled automatically by Azure CNI or kubenet,
But since we’re manually setting up Kubernetes on raw Azure VMs, we need to install a CNI ourselves.
🔎 Quick Demo
After installing Flannel:
kubectl get pods -A
All pods, including CoreDNS, start running because pod IPs are now allocated and routing works across the cluster.
Task 1: Create a ConfigMap for Your Deployment
apiVersion: v1
kind: ConfigMap
metadata: name: nginx-config-1
namespace: default
data: index.html:
This is Deployment #1
Step 2 - Apply ConfigMap
kubectl apply -f nginx-configmap.yml
Step 3 - Update Deployment to Use ConfigMap
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1 spec: replicas: 2
selector: matchLabels:
app: nginx
template: metadata:
labels: app:
nginx spec:
containers: - name: nginx
image: nginx ports: - containerPort: 80
volumeMounts: -
name: nginx-index-config
mountPath: /usr/share/nginx/html
volumes: -
name: nginx-index-config
configMap: name: nginx-config-1
Apply deployment:
kubectl apply -f nginx-deployment.yml
Step 4 - Create and Deploy Service
apiVersion: v1
kind: Service
metadata: name: nginx-service
spec: selector:
app: nginx
type: NodePort
ports:name: http
port: 80
targetPort: 80
nodePort: 30080
kubectl apply -f nginx-service.yml
Step 5 - Test from Azure VM
curl http://:30080
Task 2: Create a Secret for Your Deployment
Step 1 - Create Secret YAML
apiVersion: v1
kind: Secret
metadata: name: mysql-secret
type: Opaque stringData:
username: admin
password: password
Apply Secret:
kubectl apply -f mysql-secret.yml
Step 2 - Use Secret in Deployment
apiVersion: apps/v1
kind: Deployment
metadata: name: mysql
labels: app: mysql
spec: replicas: 1
selector: matchLabels: app: mysql
template: metadata: labels:
app: mysql
spec: containers:
- name: mysql
image: mysql:8 ports: -
containerPort: 3306
env: -
name: MYSQL_ROOT_PASSWORD
valueFrom: secretKeyRef:
name: mysql-secret
key: password
Step 3 - Verify Secret
kubectl get secrets kubectl describe secret mysql-secret
Security Best Practices:
Never commit secrets to GitHub
Use Azure Key Vault + Secrets Store CSI Driver for enterprise workloads
Enable encryption at rest for Kubernetes Secrets
Limit access via RBAC
Lessons Learned
ConfigMaps simplify configuration management
Secrets protect sensitive data like credentials
Kubernetes + Azure Key Vault is a strong combo for secure workloads
Integrating Azure Key Vault with Kubernetes (Secrets Store CSI Driver)
While Kubernetes Secrets are useful, they are only base64-encoded and require extra configuration for encryption at rest. In enterprise deployments on Azure, the recommended approach is to use Azure Key Vault with the Secrets Store CSI Driver.
This integration allows pods to securely mount secrets, keys, and certificates directly from Azure Key Vault into your containers, without embedding sensitive data in Kubernetes manifests.
Step 1 - Install Secrets Store CSI Driver
On your Azure VM (with kubectl configured for your cluster):
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/secrets-store-csi-driver/main/deploy/rbac-secretproviderclass.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/secrets-store-csi-driver/main/deploy/csidriver.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/secrets-store-csi-driver/main/deploy/secrets-store.csi.x-k8s.io_secretproviderclasses.yaml
Or, if you’re using Helm:
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi-secrets secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system
Step 2 - Create an Azure Key Vault and Store Secrets
# Create Key Vault
az keyvault create --name myKeyVault123 --resource-group k8s-rg --location eastus
# Add secrets
az keyvault secret set --vault-name myKeyVault123 --name "mysql-username" --value "admin"
az keyvault secret set --vault-name myKeyVault123 --name "mysql-password" --value "P@ssw0rd123"
Step 3 - Set Up Azure Identity for Kubernetes
We need to allow Kubernetes pods to access Key Vault. For simplicity, let’s use Azure AD Workload Identity (newer than AAD Pod Identity).
az aks update --resource-group k8s-rg --name myAKSCluster --enable-oidc-issuer --enable-workload-identity
Create a Managed Identity and assign it to Key Vault:
az identity create --name k8s-mi --resource-group k8s-rg --location eastus
# Assign Key Vault access policy
az keyvault set-policy -n myKeyVault123 --secret-permissions get list --spn <client-id-of-managed-identity>
Step 4 - Create a SecretProviderClass in Kubernetes
This tells Kubernetes which secrets from Key Vault should be mounted into the pod.
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-kv-mysql
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: <client-id-of-managed-identity>
keyvaultName: myKeyVault123
objects: |
array:
- |
objectName: mysql-username
objectType: secret
- |
objectName: mysql-password
objectType: secret
tenantId: <your-tenant-id>
Save this as spc-mysql.yaml
and apply it:
kubectl apply -f spc-mysql.yaml
Step 5 - Update Deployment to Mount Key Vault Secrets
Now modify your MySQL deployment to use the CSI driver:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_USER
valueFrom:
secretKeyRef:
name: mysql-secrets-kv
key: mysql-username
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets-kv
key: mysql-password
volumeMounts:
- name: secrets-store-inline
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "azure-kv-mysql"
Step 6 - Verify Mounted Secrets
Once deployed, check if secrets are mounted:
kubectl exec -it deploy/mysql -- ls /mnt/secrets-store
Output should list:
mysql-username
mysql-password
And you can cat them:
kubectl exec -it deploy/mysql -- cat /mnt/secrets-store/mysql-username
kubectl exec -it deploy/mysql -- cat /mnt/secrets-store/mysql-password
Benefits of Azure Key Vault + CSI Driver
✅ Centralized secrets management across apps and clusters
✅ Automatic secret rotation without redeploying pods
✅ No secrets exposed in Kubernetes manifests
✅ Works seamlessly with RBAC and Azure AD
Conclusion:
ConfigMaps and Secrets are foundational tools for managing application configs and sensitive information in Kubernetes. By practicing proper separation of concerns and leveraging Azure VM + Kubernetes setup, you can build secure, scalable, and maintainable applications.
FAQs
Can I update a ConfigMap without restarting the pod? Yes, but pods need to reload config. You may need to restart or use subPath volumes for updates.
Are Kubernetes Secrets encrypted by default? No. They are base64-encoded but can be encrypted at rest using KMS providers.
How do I use Azure Key Vault instead of plain Secrets? Use the Secrets Store CSI Driver to sync Azure Key Vault secrets into Kubernetes pods.
What’s the difference between ConfigMap and Secret volume mounts? Both can be mounted as files. The difference is sensitivity: use ConfigMaps for general configs, Secrets for sensitive values.
Can ConfigMaps be used across multiple namespaces? No. ConfigMaps and Secrets are namespace-scoped, but you can duplicate them across namespaces if needed.
Subscribe to my newsletter
Read articles from Pratiksha kadam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
