In the world of Kubernetes, separating your application code from your configuration is a fundamental best practice. It allows you to build a single Docker image and deploy it across dev, staging, and production environments simply by swapping out the configuration.
Kubernetes provides two primary resources to handle this: ConfigMaps and Secrets. While they seem incredibly similar at a glance, using them incorrectly can lead to severe security vulnerabilities or rigid deployments. In this complete guide, we will break down the differences, show you how to mount them using both environment variables and volumes, and expose the most common security misconception surrounding Kubernetes Secrets.
The Core Difference
Let's get straight to the point:
- ConfigMaps are used for non-confidential data. Think database URLs, debug flags, language settings, and Nginx configuration files.
- Secrets are used for confidential, sensitive data. Think database passwords, API keys, SSH keys, and TLS certificates.
Mechanically, they are injected into Pods in the exact same ways. The primary difference is intent and how the Kubernetes API handles them.
The Great Secret Misconception
The most dangerous misconception in Kubernetes is the belief that Secrets are encrypted. They are not.
By default, Kubernetes Secrets are simply Base64 encoded. If an attacker gains access to your cluster's API, or if a developer runs kubectl get secret my-secret -o yaml, they can trivially decode the Base64 string and steal your credentials. Furthermore, they are stored in plaintext in the cluster's underlying etcd datastore.
How to Actually Secure Secrets
To make Secrets truly secure in a production environment, you must implement two things:
- Strict RBAC (Role-Based Access Control): Ensure that only specific service accounts and highly privileged administrators have the
getandwatchpermissions for thesecretsresource. - Encryption at Rest: You must configure your Kubernetes API server to encrypt Secret data before it is written to
etcd. Most managed cloud providers (like EKS, GKE, and AKS) offer this via KMS integration.
Method 1: Mounting as Environment Variables
The easiest way to consume a ConfigMap or Secret is by injecting it as an environment variable.
When to use this method:
- For simple key-value pairs (e.g.,
DB_HOST=postgres). - When your application expects standard environment variables.
The drawback:
Environment variables are static. If you update the ConfigMap or Secret later, the running Pod will not see the changes. You must manually restart or recreate the Pod for the new values to take effect.
Example Manifest
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-app-container
image: my-app:latest
env:
# Pulling from a ConfigMap
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: app-config
key: db-url
# Pulling from a Secret
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: db-password Method 2: Mounting as Volumes
Alternatively, you can mount a ConfigMap or Secret as a file within the container's filesystem.
When to use this method:
- For large configuration files, such as an
nginx.conf, an applicationsettings.json, or a TLS certificate. - When you need hot-reloading. When mounted as a volume, Kubernetes uses a symlink mechanism (kubelet sync) to update the files inside the container automatically if the underlying ConfigMap or Secret is updated.
Example Manifest
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-app
spec:
containers:
- name: nginx-container
image: nginx:latest
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
- name: secret-volume
mountPath: /etc/nginx/ssl
volumes:
- name: config-volume
configMap:
name: nginx-config
- name: secret-volume
secret:
secretName: nginx-tls-secret Creating ConfigMaps and Secrets via CLI
While you should ideally manage these resources declaratively via GitOps, you can create them imperatively using kubectl:
# Create a ConfigMap from a literal value
kubectl create configmap my-config --from-literal=LOG_LEVEL=debug
# Create a ConfigMap from an entire file
kubectl create configmap nginx-config --from-file=nginx.conf
# Create a Secret (Docker registry example)
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password=supersecret Connecting to Ingress Controllers
One of the most critical uses for Kubernetes Secrets is terminating TLS/SSL traffic. When using an Ingress Controller, you will create a Secret of type kubernetes.io/tls containing your public certificate and private key, and reference it in your Ingress manifest.
If you are setting up routing, use our Kubernetes Ingress Generator to automatically scaffold the routing rules and TLS secret bindings.
Conclusion
Mastering ConfigMaps and Secrets is essential for building scalable, environment-agnostic deployments. Remember: use ConfigMaps for plain text configuration, use Secrets for sensitive data, configure Encryption at Rest, and choose your mounting strategy based on whether you need dynamic file updates or simple environment variables.