What are ConfigMaps and Secrets in Kubernetes?

Pratiksha kadamPratiksha kadam
8 min read

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?

  1. Simplicity → Flannel is lightweight and super easy to deploy.

     kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    
  2. 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.

  3. Pod Networking → Without Flannel, kubectl get pods will show pods stuck in Pending 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 during kubeadm init).
  4. 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

  1. 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.

  2. Are Kubernetes Secrets encrypted by default? No. They are base64-encoded but can be encrypted at rest using KMS providers.

  3. 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.

  4. 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.

  5. Can ConfigMaps be used across multiple namespaces? No. ConfigMaps and Secrets are namespace-scoped, but you can duplicate them across namespaces if needed.

0
Subscribe to my newsletter

Read articles from Pratiksha kadam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pratiksha kadam
Pratiksha kadam