Ever spent an hour debugging a broken deployment only to find a typo in an environment variable? ConfigMaps fail silently, Secrets get Base64 mangled, and a missing DB_PASSWORD brings down your entire stack. Kubernetes gives you three ways to inject environment variables into pods, and each one has gotchas that can waste your afternoon. This guide walks through direct values, ConfigMaps, and Secrets with real YAML examples, plus the verification commands that catch issues before they hit production.
Direct Methods for Setting Environment Variables in Kubernetes Containers

The env field under spec.containers gives you the most straightforward way to inject key-value pairs directly into your containers. You list each environment variable with a name property and a corresponding value property right in your YAML manifest. When Kubernetes spins up the container, these variables become available to any process running inside.
In production, you’ll want Deployment objects instead of standalone Pods. Deployments give you automatic pod rescheduling when nodes fail, rolling update capabilities, and declarative replica management. The environment variable configuration sits at spec.template.spec.containers within the Deployment structure. You’re defining the pod template that the Deployment controller will use to create and manage your pods. This nested structure becomes the foundation for all environment variable configuration in Kubernetes.
The syntax requires two properties for each variable. The name field specifies the environment variable name as it appears inside the container. The value field contains the actual data. You can set strings, numbers, or boolean values, but everything gets stored as a string representation. When your application reads these variables at runtime, you’ll need to parse them into the appropriate data type if necessary.
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-deployment
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: app-container
image: nginx:latest
env:
- name: APP_ENV
value: "production"
- name: PORT
value: "8080"
- name: MAX_CONNECTIONS
value: "100"
- name: ENABLE_METRICS
value: "true"
Run kubectl apply -f deployment.yaml to deploy this configuration to your cluster. Verify the deployment with kubectl get deployments to confirm all replicas are running. Deployments automatically handle pod failures by creating replacements, and they support rolling updates when you modify environment variable values in your manifest.
Comprehensive Methods for Injecting Configuration into Kubernetes Deployments

Kubernetes offers three complementary methods for setting environment variables, each suited to different configuration needs. Direct values work well for simple static settings that rarely change. ConfigMaps handle reusable non-sensitive data that might be shared across multiple deployments or updated independently from your application code. Secrets manage credentials, API keys, and other sensitive information that requires access control.
The choice depends on whether your data needs reusability, requires security controls, or changes frequently enough to warrant separation from deployment manifests. ConfigMaps make sense when you have configuration that multiple applications share, or when you want to update values without rebuilding container images. Secrets apply to passwords, OAuth tokens, SSH keys, and database credentials. Direct values suit one-off settings like application specific constants or environment identifiers that never contain sensitive information.
ConfigMap Method for Non-Sensitive Configuration
ConfigMaps are Kubernetes objects that store configuration data as key-value pairs in a data field. Each key becomes available as an environment variable name, and the corresponding value contains the configuration data as a UTF-8 string. This separation lets you update configuration without touching deployment manifests or rebuilding containers.
Create a ConfigMap using kubectl create configmap app-config --from-literal=LOG_LEVEL=info --from-literal=API_TIMEOUT=30 --from-literal=CACHE_TTL=3600. This command builds a ConfigMap named app-config with three key-value pairs. Verify creation with kubectl get configmap app-config -o yaml.
You can reference ConfigMap values two ways. The valueFrom approach with configMapKeyRef pulls specific keys into named environment variables:
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-selective
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: app-container
image: nginx:latest
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: API_TIMEOUT
valueFrom:
configMapKeyRef:
name: app-config
key: API_TIMEOUT
The envFrom approach with configMapRef imports every key-value pair from the ConfigMap as environment variables:
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-bulk
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: app-container
image: nginx:latest
envFrom:
- configMapRef:
name: app-config
Run kubectl apply -f deployment.yaml after creating the ConfigMap. The selective method gives you control over which variables get exposed. The bulk import method reduces YAML verbosity when you want all ConfigMap values available.
Secret Method for Sensitive Credentials
Secrets handle sensitive data like passwords, OAuth tokens, database credentials, and TLS certificates. They work identically to ConfigMaps at the YAML level, but Kubernetes stores Secret values in Base64 encoded form and provides access control mechanisms through RBAC policies. When a Secret value gets injected into a container, Kubernetes automatically decodes it back to plaintext.
Create a Secret with kubectl create secret generic db-credentials --from-literal=DB_USER=admin --from-literal=DB_PASSWORD=octoberfest --from-literal=DB_HOST=mysql.prod.svc.cluster.local. Check that it exists using kubectl get secret db-credentials.
Reference specific Secret keys using secretKeyRef in your deployment manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-app
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: api-container
image: myapp:latest
env:
- name: DATABASE_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USER
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
Import all Secret values at once using envFrom with secretRef:
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-full
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: api-container
image: myapp:latest
envFrom:
- secretRef:
name: db-credentials
Secrets provide access control rather than encryption by default. Values sit in etcd as Base64 encoded strings, which provides minimal obfuscation but not cryptographic protection. Enable encryption at rest in your cluster configuration for actual security. The Base64 decoding happens automatically when Kubernetes injects Secret values into containers, so your application receives plaintext credentials.
Combined Approach in Production Deployments
Production deployments typically mix all three methods in the same container specification. Direct values handle simple constants, ConfigMaps manage shared configuration, and Secrets protect credentials. This complete example shows how they work together:
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-api
spec:
replicas: 3
selector:
matchLabels:
app: production-api
template:
metadata:
labels:
app: production-api
spec:
containers:
- name: api-container
image: company/api:v2.1.0
env:
- name: APP_NAME
value: "production-api"
- name: CLUSTER_REGION
value: "us-east-1"
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: API_TIMEOUT
valueFrom:
configMapKeyRef:
name: app-config
key: API_TIMEOUT
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: api-secrets
key: JWT_SECRET
envFrom:
- configMapRef:
name: feature-flags
Deploy with kubectl apply -f production-deployment.yaml. This pattern separates concerns. Environment identifiers go directly in the manifest, reusable settings come from ConfigMaps, and sensitive data flows through Secrets. The feature-flags ConfigMap gets bulk imported while critical credentials use selective reference to maintain clarity about which sensitive values are in use.
Verifying and Troubleshooting Environment Variables in Kubernetes Pods

After deploying configurations, you need to verify that environment variables actually made it into running containers with the correct values. Configuration issues often show up as application startup failures or connection errors. Checking environment variables is usually the first debugging step. The kubectl command line interface provides several ways to inspect what your containers see at runtime.
The most direct check uses kubectl exec to run commands inside a container. Run kubectl exec -it deployment/webapp-deployment -- printenv to list all environment variables in the first pod of your deployment. This shows everything the container process can access, including variables you set plus Kubernetes defaults like SERVICE_HOST values. For specific variables, use kubectl exec -it deployment/webapp-deployment -- printenv LOG_LEVEL or kubectl exec -it deployment/webapp-deployment -- sh -c 'echo $LOG_LEVEL' to print individual values.
The kubectl describe pod command shows environment variable sources in the pod specification without accessing the running container. Find a pod name with kubectl get pods, then run kubectl describe pod webapp-deployment-abc123-xyz to see the Environment section. This displays whether each variable comes from a direct value, ConfigMapKeyRef, or SecretKeyRef, which helps identify misconfigured references.
Print all environment variables in a running container with kubectl exec -it <pod-name> -- printenv. This shows every variable the container can see, useful for confirming values actually made it into the runtime environment. Inspect pod specification and configuration sources using kubectl describe pod <pod-name>. It reveals where each environment variable comes from (direct value, ConfigMap, or Secret) and highlights missing references with error messages.
Check application logs for configuration errors with kubectl logs <pod-name>. This displays container output, where many applications print configuration values at startup or log errors when environment variables are missing or malformed. Verify ConfigMap contents and availability using kubectl get configmap <configmap-name> -o yaml. This shows the actual key-value pairs stored in the ConfigMap, confirming typos in key names or unexpected values. Confirm Secret existence and key names with kubectl get secret <secret-name> -o yaml, which displays Secret keys (values remain Base64 encoded in output), helping identify mismatches between the keys you reference in deployments and what actually exists in the Secret.
Environment Variable Configuration Best Practices for Kubernetes

Production environments demand clear separation between configuration types. ConfigMaps should store all non-sensitive data like API endpoints, timeout values, and feature flags. Secrets exclusively handle credentials, encryption keys, and authentication tokens. This separation aligns with the twelve-factor app methodology principle of storing configuration in the environment while maintaining security boundaries through Kubernetes RBAC policies that can grant ConfigMap access broadly while restricting Secret permissions.
For enhanced security beyond Kubernetes native Secrets, integrate external secrets management systems. The Secrets Store CSI driver project connects Kubernetes to AWS Secrets Manager, Azure Key Vault, Google Secret Manager, and HashiCorp Vault, pulling credentials directly from these services into your pods. This approach centralizes secret rotation, provides audit logging, and encrypts secrets with dedicated key management services instead of relying on etcd encryption.
Never hardcode sensitive values directly in YAML manifests, even for development environments. Manifests typically end up in version control where credentials become visible to anyone with repository access. Use descriptive naming conventions with environment specific prefixes like PROD_DB_HOST or DEV_API_URL to prevent accidentally using production credentials in development clusters.
Implement RBAC permissions that restrict Secret access to specific service accounts and namespaces, ensuring only pods that need credentials can read them. Store ConfigMap and Secret manifests in version control systems like git, but keep actual Secret values in a separate secure system and reference them through CI/CD pipelines during deployment.
Use separate ConfigMaps and Secrets for each environment (development, staging, production) rather than trying to share configuration across clusters. This reduces the risk of deploying wrong values. Implement immutable ConfigMaps and Secrets by setting immutable: true in their manifests when values shouldn’t change after creation, improving cluster performance by letting the API server stop watching for updates.
Validate that required environment variables exist at application startup, failing fast with clear error messages if critical configuration is missing rather than trying to run with defaults. Consider using Kubernetes operators like External Secrets Operator or Sealed Secrets to automate secret synchronization and enable GitOps workflows without committing plaintext credentials.
Practical Patterns for Multi-Environment and Microservices Deployments

Different environments require different configuration values for the same application. Development clusters typically use verbose DEBUG logging to help engineers troubleshoot issues. Production runs with ERROR level logging to reduce noise and storage costs. API endpoints change between environments since development might point to mock services while production connects to actual backends. Resource limits differ too. Development allows higher memory usage for profiling while production enforces strict limits to maintain cluster stability.
Namespace based separation provides the cleanest approach to managing environment specific configuration. Create namespaces called development, staging, and production. Then deploy separate ConfigMaps and Secrets in each namespace with identical names but different values. Your deployment manifests stay the same across environments, just targeting different namespaces during kubectl apply -f deployment.yaml -n production execution. This pattern prevents configuration drift and makes environment promotion straightforward.
Helm charts offer templating capabilities that let you parameterize environment variable values. Define a values-dev.yaml and values-prod.yaml file with environment specific settings, then run helm install myapp ./chart -f values-prod.yaml to render templates with the correct values. The chart template references variables like {{ .Values.logLevel }} which get replaced during installation. Kustomize provides an alternative overlay based approach where you maintain a base configuration and overlay patches for each environment.
In a microservices architecture, environment variables enable service discovery by referencing Kubernetes Service names. A backend pod connects to MySQL using DB_HOST=mysql.production.svc.cluster.local where mysql is the Service name, production is the namespace, and svc.cluster.local is the cluster domain. This pattern works across environments when each namespace contains a mysql Service, even if the underlying endpoints differ. The application code never needs environment specific logic since Kubernetes DNS handles resolution.
Feature flags implemented as environment variables let you enable functionality in specific environments without code changes. Set FEATURE_FLAG_NEW_UI=true in staging to test new features with a production like dataset before enabling in production. Boolean variables like ENABLE_METRICS=true or USE_CACHE=false control optional behaviors. Database connection patterns typically split configuration across multiple variables: DB_HOST=postgres.prod.svc.cluster.local, DB_PORT=5432, DB_USER from ConfigMap, DB_PASSWORD from Secret, and DB_NAME=orders_db as a direct value.
| Use Case | Environment Variable Pattern | Example Value |
|---|---|---|
| Service Discovery | SERVICE_HOST= |
auth-service.production.svc.cluster.local |
| Database Connection | DB_HOST, DB_PORT, DB_USER (ConfigMap), DB_PASSWORD (Secret), DB_NAME | postgres.prod.svc.cluster.local, 5432, app_user, [secret], orders |
| Feature Flags | FEATURE_FLAG_ |
FEATURE_FLAG_NEW_CHECKOUT=true |
| Logging Level | LOG_LEVEL=DEBUG|INFO|WARN|ERROR | DEBUG (dev), ERROR (prod) |
| API Endpoints | BACKEND_URL=https://api. |
https://api.staging.company.com |
| Resource Limits | MAX_MEMORY_MB, MAX_CPU_CORES, CONNECTION_POOL_SIZE | 2048, 2, 50 (prod); 4096, 4, 100 (dev) |
GitOps workflows manage environment specific configuration by storing manifests in git with directory structures like k8s/base/, k8s/overlays/dev/, and k8s/overlays/prod/. The base directory contains shared deployment manifests, while overlay directories hold environment specific ConfigMaps and Secrets. Tools like ArgoCD or Flux watch these repositories and automatically apply changes when you commit configuration updates, maintaining audit trails of who changed what values and when. This approach combines version control benefits with automated deployment while keeping environment configurations isolated.
Final Words
Setting environment variables in Kubernetes gives you clean separation between code and configuration.
Start with direct env fields for simple cases. Reach for ConfigMaps when you need reusable non-sensitive data across multiple deployments. Use Secrets for anything credential-related.
The verification commands matter. Run kubectl exec and kubectl describe pod after every deployment to catch config issues before they hit production.
Combine all three methods in the same Deployment manifest when your app needs it. That’s what production looks like.
You’ve got the tools. Now go wire up your containers with the config they need to run.
FAQ
How do you set environment variables in Kubernetes?
You set environment variables in Kubernetes by adding the env field under spec.containers in your pod or Deployment YAML manifest, where each variable requires a name and value property. Apply the configuration using kubectl apply -f your-manifest.yaml to inject the variables directly into your container.
What is the best practice for environment variables in Kubernetes?
The best practice for environment variables in Kubernetes separates ConfigMaps for non-sensitive data and Secrets for credentials, implements RBAC controls, uses descriptive naming conventions, and stores manifests in version control. Never hardcode sensitive values directly in YAML files.
How do you set environment variables in a container?
You set environment variables in a container by defining the env field within the container specification in your Deployment or Pod manifest using direct key-value pairs, or by referencing ConfigMaps and Secrets with valueFrom and envFrom fields. The variables become available when the container starts.
How do you set JAVA_HOME in environment variables for Kubernetes?
You set JAVAHOME in Kubernetes environment variables by adding it to the env section of your container spec with name: JAVA_HOME and value: /usr/lib/jvm/java-11-openjdk-amd64 or your specific Java path. Apply the manifest to make JAVAHOME available to your Java applications.
How do you reference ConfigMaps in environment variables?
You reference ConfigMaps in environment variables using valueFrom with configMapKeyRef to select specific keys, or use envFrom with configMapRef to import all ConfigMap values at once. Both methods require the ConfigMap to exist before deploying the pod.
How do you use Secrets for environment variables?
You use Secrets for environment variables by creating a Secret with kubectl create secret generic, then referencing it in your manifest using secretKeyRef for individual keys or secretRef for bulk import. Kubernetes automatically Base64-decodes Secret values before injecting them into containers.
How do you verify environment variables in running pods?
You verify environment variables in running pods using kubectl exec podname -- printenv to display all variables, or kubectl describe pod podname to view the configuration sources. Check application startup logs with kubectl logs to confirm your app reads the values correctly.
How do you manage environment variables across multiple environments?
You manage environment variables across multiple environments using separate namespaces for dev, staging, and production, with different ConfigMaps and Secrets per environment. Helm charts or Kustomize overlays let you template variable values based on deployment context.
What’s the difference between env and envFrom in Kubernetes?
The difference between env and envFrom is that env selects individual variables one at a time with explicit name-value pairs or references, while envFrom imports all keys from a ConfigMap or Secret in one statement. Use envFrom when you need bulk configuration import.
How do you troubleshoot missing environment variables?
You troubleshoot missing environment variables by running kubectl describe pod to check if ConfigMaps or Secrets exist and are correctly referenced, then use kubectl exec with printenv to verify container runtime values. Check pod events for mounting errors or missing resources.
Can you combine direct values, ConfigMaps, and Secrets in one deployment?
You can combine direct values, ConfigMaps, and Secrets in one deployment by listing multiple env entries that use different sources in the same container specification. This pattern lets you mix static config, reusable settings, and credentials in production deployments.
How do you update environment variables in running deployments?
You update environment variables in running deployments by editing the ConfigMap or Secret, then triggering a pod restart using kubectl rollout restart deployment/deployment-name to pick up the new values. Kubernetes doesn’t hot-reload environment variables automatically.
