ConfigMaps & Secrets: Configure Python in Kubernetes
ConfigMaps and Secrets are Kubernetes resources for managing application configuration and sensitive data. A ConfigMap stores non-sensitive configuration (database URLs, feature flags, log levels) as key-value pairs or files. A Secret stores sensitive data (passwords, API keys, credentials) and is encrypted at rest (in etcd) and in transit. Rather than baking configuration into your Python container image, you externalize it to ConfigMaps and Secrets, allowing the same image to run in development, staging, and production with different configurations.
What Is a ConfigMap and How Do You Create One?
A ConfigMap is a Kubernetes resource that holds non-sensitive key-value configuration data. You create a ConfigMap via YAML or the kubectl create configmap command. Here's a ConfigMap for a Python Flask application:
apiVersion: v1
kind: ConfigMap
metadata:
name: python-app-config
namespace: default
data:
DATABASE_URL: "postgresql://user:pass@db-svc:5432/appdb"
LOG_LEVEL: "INFO"
DEBUG: "false"
API_TIMEOUT: "30"
Or create it from the command line:
kubectl create configmap python-app-config \
--from-literal=DATABASE_URL="postgresql://user:pass@db-svc:5432/appdb" \
--from-literal=LOG_LEVEL=INFO \
--from-literal=DEBUG=false
You can also create a ConfigMap from a file:
# Create a config file
cat > app.conf << EOF
[database]
host=db-svc
port=5432
name=appdb
EOF
# Create ConfigMap from file
kubectl create configmap python-app-config --from-file=app.conf
Injecting ConfigMap Data into Python Pods
There are two ways to inject ConfigMap data into a pod: as environment variables or as mounted files (volumes).
Injecting as Environment Variables
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: app
image: my-registry/python-app:1.0.0
envFrom:
- configMapRef:
name: python-app-config
ports:
- containerPort: 8000
The envFrom field injects all key-value pairs from the ConfigMap as environment variables. Your Python code accesses them via os.environ:
import os
database_url = os.getenv("DATABASE_URL")
log_level = os.getenv("LOG_LEVEL", "DEBUG") # Default to DEBUG if not set
print(f"Connecting to {database_url}")
print(f"Log level: {log_level}")
Injecting as Mounted Files
For larger configuration files, mount the ConfigMap as a volume:
apiVersion: v1
kind: Deployment
metadata:
name: python-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: app
image: my-registry/python-app:1.0.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
ports:
- containerPort: 8000
volumes:
- name: config-volume
configMap:
name: python-app-config
items:
- key: app.conf
path: app.conf
Your Python code reads the file:
import configparser
config = configparser.ConfigParser()
config.read("/etc/config/app.conf")
db_host = config.get("database", "host")
db_port = config.get("database", "port")
Understanding Kubernetes Secrets and Secure Data Storage
Secrets store sensitive data. Kubernetes encrypts Secrets at rest in etcd (though this must be enabled—many clusters do not enable encryption by default). Secrets come in several types: Opaque (generic key-value), kubernetes.io/basic-auth, kubernetes.io/ssh-auth, tls (certificates), and others.
Create a Secret for database credentials:
kubectl create secret generic python-db-secret \
--from-literal=database-user=appuser \
--from-literal=database-password=SecureP@ssw0rd \
--from-literal=api-key=sk-1234567890abcdef
Or via YAML (note: values are base64-encoded for readability, not encryption—this is why Secrets are not truly secure without etcd encryption):
apiVersion: v1
kind: Secret
metadata:
name: python-db-secret
type: Opaque
data:
database-user: YXBwdXNlcg== # base64 of "appuser"
database-password: U2VjdXJlUEBzc3cwcmQ= # base64 of "SecureP@ssw0rd"
api-key: c2stMTIzNDU2Nzg5MGFiY2RlZg== # base64 of "sk-1234567890abcdef"
Injecting Secrets into Python Pods
Inject Secrets the same way as ConfigMaps—as environment variables or volumes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: app
image: my-registry/python-app:1.0.0
env:
- name: DATABASE_USER
valueFrom:
secretKeyRef:
name: python-db-secret
key: database-user
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: python-db-secret
key: database-password
- name: API_KEY
valueFrom:
secretKeyRef:
name: python-db-secret
key: api-key
ports:
- containerPort: 8000
Your Python application reads these environment variables:
import os
import psycopg2
db_user = os.getenv("DATABASE_USER")
db_password = os.getenv("DATABASE_PASSWORD")
api_key = os.getenv("API_KEY")
# Connect to database
conn = psycopg2.connect(
host="db-svc",
port=5432,
database="appdb",
user=db_user,
password=db_password
)
Best Practices: ConfigMaps vs Secrets
| Aspect | ConfigMap | Secret |
|---|---|---|
| Data Type | Non-sensitive configuration | Sensitive credentials, keys |
| Size Limit | 1 MB | 1 MB |
| Encryption | At rest only if enabled | Yes (if etcd encryption enabled) |
| Use Case | Log levels, feature flags, URLs | Passwords, API keys, certificates |
| Audit | Not audited by default | Should be audited for security |
Never store credentials in ConfigMaps. Always use Secrets, and ensure your Kubernetes cluster has etcd encryption enabled. Even better, integrate with external secret management tools (Vault, AWS Secrets Manager) via operators like External Secrets Operator.
Mounting Secrets as Files for TLS Certificates
For TLS certificates, mount a Secret as a volume:
apiVersion: v1
kind: Secret
metadata:
name: python-app-tls
type: tls
data:
tls.crt: <base64-encoded-certificate>
tls.key: <base64-encoded-key>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-app-deployment
spec:
replicas: 1
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: app
image: my-registry/python-app:1.0.0
volumeMounts:
- name: tls-certs
mountPath: /etc/tls
readOnly: true
ports:
- containerPort: 8443
volumes:
- name: tls-certs
secret:
secretName: python-app-tls
items:
- key: tls.crt
path: cert.pem
- key: tls.key
path: key.pem
Your Python app uses the certificates for HTTPS:
import ssl
from flask import Flask
app = Flask(__name__)
if __name__ == "__main__":
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(
certfile="/etc/tls/cert.pem",
keyfile="/etc/tls/key.pem"
)
app.run(host="0.0.0.0", port=8443, ssl_context=ssl_context)
Key Takeaways
- ConfigMaps store non-sensitive configuration; Secrets store sensitive credentials and keys.
- Inject ConfigMaps and Secrets as environment variables (for simple key-value) or mounted volumes (for files or certificates).
- Kubernetes encodes Secret values in base64, but encryption at rest requires enabling etcd encryption.
- Never store credentials in ConfigMaps or container images; always use Secrets.
- For production, consider external secret management (Vault, AWS Secrets Manager) via operators.
Frequently Asked Questions
Is base64 encoding in Secrets secure?
No, base64 is encoding, not encryption. Anyone with kubectl access can decode Secret values with kubectl get secret python-db-secret -o yaml. You must enable etcd encryption and restrict RBAC access to Secrets. Even better, use external secret managers.
Can I update a ConfigMap without redeploying pods?
Yes, but pods do not automatically reload ConfigMap data. If you inject via envFrom, you must restart pods for changes to take effect. If you mount as a volume, Kubernetes updates the mounted files automatically (usually within ~1 minute), and your application code should watch for changes or be reloaded.
How do I use Docker image secrets (private registries) in Kubernetes?
Create an image pull secret and reference it in the Deployment:
kubectl create secret docker-registry registry-secret \
--docker-server=my-registry.azurecr.io \
--docker-username=<username> \
--docker-password=<password>
Then in your Deployment spec:
spec:
imagePullSecrets:
- name: registry-secret
What happens if a Secret is deleted while pods are running?
If a Secret is mounted as a volume and deleted, Kubernetes removes the volume from new pods but existing pods retain the mounted files (read from the node's local cache). This is a graceful degradation but also a security risk—deleted Secrets may linger in memory or logs.
Should I version my ConfigMaps and Secrets?
Yes, a best practice is to version ConfigMaps by name (e.g., python-app-config-v1, python-app-config-v2) and update Deployments to reference the new version. This allows you to roll back to previous configurations if needed.