Learning Kubernetes - Episode 23 - Environment Variable

Learning Kubernetes - Episode 23 - Environment Variable

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.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 29, 2026
0 views
9 min read

Introduction

Note

If you want to read the previous episode, you can click the Episode 22 thumbnail below

Episode 22Episode 22

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.

What Are Environment Variables?

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:

  • Configuration injection - Pass settings to applications
  • Runtime configuration - Set at container startup
  • No code changes - Configure without rebuilding images
  • Environment-specific - Different values per environment
  • Twelve-factor app - Standard configuration method
  • Secure secrets - Can reference encrypted Secrets

Why Use Environment Variables?

Environment variables solve several configuration challenges:

  • Portability - Same image works in different environments
  • Security - Keep secrets out of code and images
  • Flexibility - Change configuration without rebuilding
  • Separation of concerns - Decouple config from code
  • Dynamic configuration - Inject Pod metadata at runtime
  • Standard practice - Industry-standard configuration method
  • Easy debugging - View configuration without inspecting code

Without environment variables, you would need different images for each environment or hardcode configuration, making deployments inflexible and insecure.

Setting Environment Variables

There are several ways to set environment variables in Kubernetes.

Direct Value Assignment

Set environment variables directly in the Pod spec.

Example: Basic Environment Variables

Kubernetesbasic-env.yml
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

Kubernetesmultiple-env.yml
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"

Using ConfigMap

Reference ConfigMap data as environment variables.

Example: ConfigMap as Environment Variables

Kubernetesconfigmap-env.yml
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.level

Example: Import All ConfigMap Keys

Kubernetesconfigmap-envfrom.yml
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-config

All ConfigMap keys become environment variables automatically.

Using Secret

Reference Secret data as environment variables for sensitive information.

Example: Secret as Environment Variables

Kubernetessecret-env.yml
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-key

Example: Import All Secret Keys

Kubernetessecret-envfrom.yml
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-secrets

Warning

Security Note: Environment variables are visible in Pod specs and container inspect commands. For highly sensitive data, consider mounting Secrets as files instead.

Downward API

Inject Pod and container metadata as environment variables.

Example: Pod Metadata as Environment Variables

Kubernetesdownward-api-env.yml
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.serviceAccountName

Example: Resource Limits as Environment Variables

Kubernetesresource-env.yml
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.cpu

Combining Multiple Sources

Mix different environment variable sources.

Example: Mixed Environment Variables

Kubernetesmixed-env.yml
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_

Practical Examples

Example 1: Web Application Configuration

Kubernetesweb-app-config.yml
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-config

Example 2: Database Connection Configuration

Kubernetesdatabase-config.yml
apiVersion: 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)"

Example 3: Multi-Environment Configuration

Kubernetesmulti-env-config.yml
# 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-prod

Example 4: Feature Flags

Kubernetesfeature-flags.yml
apiVersion: 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_

Environment Variable Substitution

Use environment variables within other environment variables.

Example: Variable Substitution

Kubernetesenv-substitution.yml
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"

Viewing Environment Variables

Check environment variables in running containers.

View All Environment Variables

Kubernetesbash
sudo kubectl exec <pod-name> -- env

View Specific Environment Variable

Kubernetesbash
sudo kubectl exec <pod-name> -- printenv DATABASE_HOST

View Environment Variables with Grep

Kubernetesbash
sudo kubectl exec <pod-name> -- env | grep DB_

Describe Pod to See Environment Variables

Kubernetesbash
sudo kubectl describe pod <pod-name>

Common Mistakes and Pitfalls

Mistake 1: Hardcoding Secrets

Problem: Putting sensitive data directly in Pod specs.

Solution: Use Secrets for sensitive data:

Kubernetesyml
# Bad: Hardcoded password
env:
    - name: DB_PASSWORD
      value: "mypassword123"
 
# Good: Reference Secret
env:
    - name: DB_PASSWORD
      valueFrom:
          secretKeyRef:
              name: db-credentials
              key: password

Mistake 2: Missing ConfigMap or Secret

Problem: Pod fails to start because referenced ConfigMap/Secret doesn't exist.

Solution: Create ConfigMap/Secret before creating Pod:

Kubernetesbash
# Check if ConfigMap exists
sudo kubectl get configmap app-config
 
# Create if missing
sudo kubectl create configmap app-config --from-literal=key=value

Mistake 3: Wrong Key Name

Problem: Referencing non-existent key in ConfigMap/Secret.

Solution: Verify key names:

Kubernetesbash
# View ConfigMap keys
sudo kubectl describe configmap app-config
 
# View Secret keys
sudo kubectl describe secret db-credentials

Mistake 4: Not Using Quotes for Values

Problem: YAML parsing issues with special characters.

Solution: Quote environment variable values:

Kubernetesyml
# Bad: May cause parsing issues
env:
    - name: SPECIAL_CHARS
      value: @#$%^&*
 
# Good: Quoted value
env:
    - name: SPECIAL_CHARS
      value: "@#$%^&*"

Mistake 5: Exposing Secrets in Logs

Problem: Logging environment variables exposes secrets.

Solution: Avoid logging sensitive environment variables:

Kubernetesyml
# Bad: Logs all environment variables
command: ["sh", "-c", "env && ./app"]
 
# Good: Don't log environment variables
command: ["./app"]

Best Practices

Use ConfigMaps for Non-Sensitive Data

Separate configuration from code:

Kubernetesyml
# Application settings in ConfigMap
configMapRef:
    name: app-config

Use Secrets for Sensitive Data

Protect credentials and keys:

Kubernetesyml
# Credentials in Secret
secretRef:
    name: app-secrets

Use Descriptive Names

Choose clear environment variable names:

Kubernetesyml
# Good: Clear and descriptive
- name: DATABASE_CONNECTION_TIMEOUT
- name: MAX_RETRY_ATTEMPTS
- name: LOG_LEVEL
 
# Avoid: Unclear abbreviations
- name: DBTIMEOUT
- name: MAXRET
- name: LL

Organize environment variables logically:

Kubernetesyml
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_MODE

Use envFrom for Multiple Variables

Import all keys from ConfigMap/Secret:

Kubernetesyml
envFrom:
    - configMapRef:
          name: app-config
    - secretRef:
          name: app-secrets

Add Prefixes to Avoid Conflicts

Prevent variable name collisions:

Kubernetesyml
envFrom:
    - configMapRef:
          name: app-config
          prefix: APP_
    - secretRef:
          name: db-credentials
          prefix: DB_

Document Environment Variables

Add comments explaining variables:

Kubernetesyml
env:
    # Maximum number of database connections
    - name: DB_MAX_CONNECTIONS
      value: "20"
    # Session timeout in seconds
    - name: SESSION_TIMEOUT
      value: "3600"

Use Default Values in Application

Handle missing environment variables gracefully:

python
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')

Updating Environment Variables

Environment variables are set at container startup. To update them:

Update ConfigMap

Kubernetesbash
sudo kubectl edit configmap app-config

Update Secret

Kubernetesbash
sudo kubectl edit secret app-secrets

Restart Pods

Changes require Pod restart:

Kubernetesbash
# Restart Deployment
sudo kubectl rollout restart deployment app
 
# Delete Pod (for standalone Pods)
sudo kubectl delete pod app-pod

Important

Important: Updating ConfigMaps or Secrets doesn't automatically update environment variables in running containers. You must restart Pods to apply changes.

Troubleshooting Environment Variables

Check Environment Variables

Kubernetesbash
sudo kubectl exec <pod-name> -- env

Verify ConfigMap

Kubernetesbash
sudo kubectl get configmap <name> -o yaml

Verify Secret

Kubernetesbash
sudo kubectl get secret <name> -o yaml

Check Pod Events

Kubernetesbash
sudo kubectl describe pod <pod-name>

Look for errors like:

  • ConfigMap not found
  • Secret not found
  • Invalid key reference

Conclusion

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:

  • Environment variables configure applications without code changes
  • Set variables directly in Pod specs with value
  • Reference ConfigMaps for non-sensitive configuration
  • Reference Secrets for sensitive data like passwords
  • Use Downward API to inject Pod metadata
  • envFrom imports all keys from ConfigMap/Secret
  • Add prefixes to avoid variable name conflicts
  • Variable substitution allows composing values
  • Environment variables are set at container startup
  • Restart Pods to apply ConfigMap/Secret changes
  • Use descriptive names for clarity
  • Document environment variables with comments
  • Handle missing variables with default values in code
  • Never hardcode secrets in Pod specs

Environment 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

Episode 24Episode 24

Related Posts