In this episode, we'll discuss Environment Variables in Kubernetes. We'll learn how to set environment variables, use ConfigMaps and Secrets, reference Pod fields, and best practices for configuration management.

Note
If you want to read the previous episode, you can click the Episode 22 thumbnail below
In the previous episode, we learned about sharing volumes between Pods using PersistentVolumes and PersistentVolumeClaims. In episode 23, we'll discuss Environment Variables, a fundamental way to configure applications in Kubernetes.
Note: Here I'll be using a Kubernetes Cluster installed through K3s.
Environment variables are key-value pairs that applications use for configuration. In Kubernetes, you can set environment variables directly in Pod specs, reference ConfigMaps and Secrets, or dynamically inject Pod metadata.
Environment Variables are dynamic values that affect how processes behave in a container. They provide a way to configure applications without modifying code or rebuilding images.
Think of environment variables like settings on your phone - you can change the language, timezone, or theme without reinstalling apps. Similarly, environment variables let you configure applications for different environments (dev, staging, production) using the same container image.
Key characteristics of Environment Variables:
Environment variables solve several configuration challenges:
Without environment variables, you would need different images for each environment or hardcode configuration, making deployments inflexible and insecure.
There are several ways to set environment variables in Kubernetes.
Set environment variables directly in the Pod spec.
Example: Basic Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: env-pod
spec:
containers:
- name: app
image: nginx:1.25
env:
- name: ENVIRONMENT
value: "production"
- name: LOG_LEVEL
value: "info"
- name: MAX_CONNECTIONS
value: "100"Example: Multiple Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: APP_NAME
value: "MyApplication"
- name: APP_VERSION
value: "1.0.0"
- name: DATABASE_HOST
value: "db.example.com"
- name: DATABASE_PORT
value: "5432"
- name: CACHE_ENABLED
value: "true"
- name: CACHE_TTL
value: "3600"Reference ConfigMap data as environment variables.
Example: ConfigMap as Environment Variables
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database.host: "db.example.com"
database.port: "5432"
cache.enabled: "true"
log.level: "info"
---
apiVersion: v1
kind: Pod
metadata:
name: configmap-env-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database.host
- name: DATABASE_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: database.port
- name: CACHE_ENABLED
valueFrom:
configMapKeyRef:
name: app-config
key: cache.enabled
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: log.levelExample: Import All ConfigMap Keys
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "db.example.com"
DATABASE_PORT: "5432"
CACHE_ENABLED: "true"
LOG_LEVEL: "info"
---
apiVersion: v1
kind: Pod
metadata:
name: envfrom-pod
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: app-configAll ConfigMap keys become environment variables automatically.
Reference Secret data as environment variables for sensitive information.
Example: Secret as Environment Variables
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: "admin"
password: "secretpassword"
api-key: "abc123xyz789"
---
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
- name: API_KEY
valueFrom:
secretKeyRef:
name: db-credentials
key: api-keyExample: Import All Secret Keys
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
DB_USERNAME: "admin"
DB_PASSWORD: "secretpassword"
API_KEY: "abc123xyz789"
---
apiVersion: v1
kind: Pod
metadata:
name: secret-envfrom-pod
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- secretRef:
name: app-secretsWarning
Security Note: Environment variables are visible in Pod specs and container inspect commands. For highly sensitive data, consider mounting Secrets as files instead.
Inject Pod and container metadata as environment variables.
Example: Pod Metadata as Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: downward-api-pod
labels:
app: myapp
version: v1.0
annotations:
description: "Example application"
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- while true; do env | grep POD; sleep 10; done
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountNameExample: Resource Limits as Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: resource-env-pod
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
env:
- name: MEMORY_REQUEST
valueFrom:
resourceFieldRef:
containerName: app
resource: requests.memory
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.memory
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: app
resource: requests.cpu
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.cpuMix different environment variable sources.
Example: Mixed Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: mixed-env-pod
spec:
containers:
- name: app
image: myapp:latest
env:
# Direct value
- name: ENVIRONMENT
value: "production"
# From ConfigMap
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database.host
# From Secret
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# From Downward API
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# Import all from ConfigMap
envFrom:
- configMapRef:
name: app-config
prefix: CONFIG_
# Import all from Secret
- secretRef:
name: app-secrets
prefix: SECRET_apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
APP_NAME: "MyWebApp"
APP_ENV: "production"
LOG_LEVEL: "info"
SESSION_TIMEOUT: "3600"
MAX_UPLOAD_SIZE: "10485760"
---
apiVersion: v1
kind: Secret
metadata:
name: web-secrets
type: Opaque
stringData:
JWT_SECRET: "your-jwt-secret-key"
ENCRYPTION_KEY: "your-encryption-key"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: mywebapp:latest
ports:
- containerPort: 8080
env:
# Pod metadata
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# Secrets
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: web-secrets
key: JWT_SECRET
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: web-secrets
key: ENCRYPTION_KEY
# Import all ConfigMap
envFrom:
- configMapRef:
name: web-configapiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
DB_HOST: "postgres.default.svc.cluster.local"
DB_PORT: "5432"
DB_NAME: "myapp"
DB_SSL_MODE: "require"
DB_MAX_CONNECTIONS: "20"
DB_TIMEOUT: "30"
---
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
DB_USERNAME: "appuser"
DB_PASSWORD: "securepassword123"
---
apiVersion: v1
kind: Pod
metadata:
name: app-with-db
spec:
containers:
- name: app
image: myapp:latest
env:
# Database host and port from ConfigMap
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: db-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: db-config
key: DB_PORT
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: db-config
key: DB_NAME
# Credentials from Secret
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
# Build connection string
- name: DATABASE_URL
value: "postgresql://$(DB_USERNAME):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)"# Development ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-dev
namespace: development
data:
ENVIRONMENT: "development"
LOG_LEVEL: "debug"
DEBUG_MODE: "true"
API_URL: "https://api-dev.example.com"
---
# Production ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-prod
namespace: production
data:
ENVIRONMENT: "production"
LOG_LEVEL: "warn"
DEBUG_MODE: "false"
API_URL: "https://api.example.com"
---
# Development Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: development
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: app-config-dev
---
# Production Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: app-config-prodapiVersion: v1
kind: ConfigMap
metadata:
name: feature-flags
data:
FEATURE_NEW_UI: "true"
FEATURE_BETA_API: "false"
FEATURE_ANALYTICS: "true"
FEATURE_DARK_MODE: "true"
FEATURE_NOTIFICATIONS: "true"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: feature-flags
prefix: FEATURE_Use environment variables within other environment variables.
Example: Variable Substitution
apiVersion: v1
kind: Pod
metadata:
name: substitution-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DB_HOST
value: "postgres.example.com"
- name: DB_PORT
value: "5432"
- name: DB_NAME
value: "myapp"
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# Compose connection string using other variables
- name: DATABASE_URL
value: "postgresql://$(DB_USERNAME):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=require"Check environment variables in running containers.
sudo kubectl exec <pod-name> -- envsudo kubectl exec <pod-name> -- printenv DATABASE_HOSTsudo kubectl exec <pod-name> -- env | grep DB_sudo kubectl describe pod <pod-name>Problem: Putting sensitive data directly in Pod specs.
Solution: Use Secrets for sensitive data:
# Bad: Hardcoded password
env:
- name: DB_PASSWORD
value: "mypassword123"
# Good: Reference Secret
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: passwordProblem: Pod fails to start because referenced ConfigMap/Secret doesn't exist.
Solution: Create ConfigMap/Secret before creating Pod:
# Check if ConfigMap exists
sudo kubectl get configmap app-config
# Create if missing
sudo kubectl create configmap app-config --from-literal=key=valueProblem: Referencing non-existent key in ConfigMap/Secret.
Solution: Verify key names:
# View ConfigMap keys
sudo kubectl describe configmap app-config
# View Secret keys
sudo kubectl describe secret db-credentialsProblem: YAML parsing issues with special characters.
Solution: Quote environment variable values:
# Bad: May cause parsing issues
env:
- name: SPECIAL_CHARS
value: @#$%^&*
# Good: Quoted value
env:
- name: SPECIAL_CHARS
value: "@#$%^&*"Problem: Logging environment variables exposes secrets.
Solution: Avoid logging sensitive environment variables:
# Bad: Logs all environment variables
command: ["sh", "-c", "env && ./app"]
# Good: Don't log environment variables
command: ["./app"]Separate configuration from code:
# Application settings in ConfigMap
configMapRef:
name: app-configProtect credentials and keys:
# Credentials in Secret
secretRef:
name: app-secretsChoose clear environment variable names:
# Good: Clear and descriptive
- name: DATABASE_CONNECTION_TIMEOUT
- name: MAX_RETRY_ATTEMPTS
- name: LOG_LEVEL
# Avoid: Unclear abbreviations
- name: DBTIMEOUT
- name: MAXRET
- name: LLOrganize environment variables logically:
env:
# Database configuration
- name: DB_HOST
- name: DB_PORT
- name: DB_NAME
# Cache configuration
- name: CACHE_HOST
- name: CACHE_PORT
- name: CACHE_TTL
# Application settings
- name: LOG_LEVEL
- name: DEBUG_MODEImport all keys from ConfigMap/Secret:
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secretsPrevent variable name collisions:
envFrom:
- configMapRef:
name: app-config
prefix: APP_
- secretRef:
name: db-credentials
prefix: DB_Add comments explaining variables:
env:
# Maximum number of database connections
- name: DB_MAX_CONNECTIONS
value: "20"
# Session timeout in seconds
- name: SESSION_TIMEOUT
value: "3600"Handle missing environment variables gracefully:
import os
# Python example with default value
db_host = os.getenv('DB_HOST', 'localhost')
db_port = int(os.getenv('DB_PORT', '5432'))
log_level = os.getenv('LOG_LEVEL', 'info')Environment variables are set at container startup. To update them:
sudo kubectl edit configmap app-configsudo kubectl edit secret app-secretsChanges require Pod restart:
# Restart Deployment
sudo kubectl rollout restart deployment app
# Delete Pod (for standalone Pods)
sudo kubectl delete pod app-podImportant
Important: Updating ConfigMaps or Secrets doesn't automatically update environment variables in running containers. You must restart Pods to apply changes.
sudo kubectl exec <pod-name> -- envsudo kubectl get configmap <name> -o yamlsudo kubectl get secret <name> -o yamlsudo kubectl describe pod <pod-name>Look for errors like:
In episode 23, we've explored Environment Variables in Kubernetes. We've learned how to set environment variables, use ConfigMaps and Secrets, reference Pod metadata, and best practices for configuration management.
Key takeaways:
valueEnvironment variables are essential for configuring applications in Kubernetes. By understanding different ways to set and manage environment variables, you can build flexible, secure, and maintainable applications.
Are you getting a clearer understanding of Environment Variables in Kubernetes? Keep your learning momentum going and look forward to the next episode!
Note
If you want to continue to the next episode, you can click the Episode 24 thumbnail below