Skip to main content

Configuration & Secrets:

 Configuration & Secrets: ConfigMaps, Secrets, and Environment Variables

📅 Published: May 2026
⏱️ Estimated Reading Time: 18 minutes
🏷️ Tags: Kubernetes, ConfigMaps, Secrets, Environment Variables, Configuration Management


Introduction: The Configuration Problem

Applications need configuration. Database connection strings, feature flags, API endpoints, environment names—these values change between development, staging, and production. They should not be hardcoded into container images.

Hardcoding configuration leads to:

  • Different images for each environment

  • Security risks from embedded secrets

  • Redeploys for simple config changes

  • Configuration drift between environments

Kubernetes solves this with ConfigMaps and Secrets. These objects separate configuration from container images, allowing you to change settings without rebuilding or redeploying.

This guide covers how to use ConfigMaps for non-sensitive configuration, Secrets for sensitive data, and environment variables for runtime configuration.


Part 1: Environment Variables in Kubernetes

What Are Environment Variables?

Environment variables are key-value pairs available to processes running in a container. They are the simplest way to pass configuration to your application.

Ways to Set Environment Variables

1. Hardcoded in Pod spec

yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: app
    image: nginx
    env:
    - name: APP_ENV
      value: "production"
    - name: LOG_LEVEL
      value: "info"
    - name: PORT
      value: "8080"

2. Using valueFrom to reference other sources

yaml
env:
- name: MY_NODE_NAME
  valueFrom:
    fieldRef:
      fieldPath: spec.nodeName
- name: MY_POD_IP
  valueFrom:
    fieldRef:
      fieldPath: status.podIP
- name: MY_POD_NAMESPACE
  valueFrom:
    fieldRef:
      fieldPath: metadata.namespace

3. Using resourceFieldRef for resource limits

yaml
env:
- name: MY_CPU_LIMIT
  valueFrom:
    resourceFieldRef:
      containerName: app
      resource: limits.cpu
- name: MY_MEMORY_REQUEST
  valueFrom:
    resourceFieldRef:
      containerName: app
      resource: requests.memory

Part 2: ConfigMaps

What is a ConfigMap?

A ConfigMap is an API object used to store non-sensitive configuration data in key-value pairs. ConfigMaps can be consumed by Pods as environment variables, command-line arguments, or configuration files mounted as volumes.

Think of a ConfigMap as a configuration file that lives inside Kubernetes. Your Pods can read from it without needing to rebuild your container image.

ConfigMap YAML Example

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  # Key-value pairs
  APP_ENV: production
  LOG_LEVEL: info
  API_BASE_URL: https://api.example.com
  
  # Multi-line values (using pipe)
  app.properties: |
    database.host=postgres.db.svc
    database.port=5432
    cache.ttl=3600

Creating ConfigMaps

bash
# From literal values
kubectl create configmap app-config --from-literal=APP_ENV=production --from-literal=LOG_LEVEL=info

# From a file
kubectl create configmap app-config --from-file=./app.properties

# From a directory (each file becomes a key)
kubectl create configmap app-config --from-file=./config/

# From an env file
kubectl create configmap app-config --from-env-file=config.env

# From YAML
kubectl apply -f configmap.yaml

Using ConfigMaps as Environment Variables

yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-configmap
spec:
  containers:
  - name: app
    image: nginx
    env:
    # Import specific key
    - name: APP_ENV
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL
    # Import all keys (envFrom)
    envFrom:
    - configMapRef:
        name: app-config

Using ConfigMaps as Volume Mounts

yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-configmap-volume
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config

When mounted as a volume, each key becomes a file containing the key's value.

text
Inside the container:
/etc/config/APP_ENV          → contains "production"
/etc/config/LOG_LEVEL        → contains "info"
/etc/config/app.properties   → contains multi-line content

ConfigMap with Items (Selective Mounting)

yaml
volumes:
- name: config-volume
  configMap:
    name: app-config
    items:
    - key: app.properties
      path: application.properties
    - key: LOG_LEVEL
      path: log/level.txt

ConfigMap Immutability

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: immutable-config
immutable: true
data:
  important-setting: "value-cannot-change"

Once immutable: true is set, the ConfigMap cannot be changed. This is useful for configuration that must not be altered after deployment.


Part 3: Secrets

What is a Secret?

A Secret is similar to a ConfigMap but designed for sensitive information: passwords, API keys, tokens, SSH keys, TLS certificates. Secrets are stored encoded (not encrypted by default) and are more tightly controlled.

Secret YAML Example

yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  # Values must be base64 encoded
  username: YWRtaW4=        # admin
  password: c3VwZXJzZWNyZXQ= # supersecret
---
# Or using stringData (plain text, Kubernetes encodes automatically)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
stringData:
  username: admin
  password: supersecret

Creating Secrets

bash
# From literal values
kubectl create secret generic db-secret --from-literal=username=admin --from-literal=password=supersecret

# From files
kubectl create secret generic db-secret --from-file=./username.txt --from-file=./password.txt

# From env file
kubectl create secret generic db-secret --from-env-file=secrets.env

# For TLS certificates
kubectl create secret tls tls-secret --cert=tls.crt --key=tls.key

# For Docker registry authentication
kubectl create secret docker-registry regcred --docker-server=myregistry.io --docker-username=user --docker-password=pass --docker-email=email@example.com

# From YAML
kubectl apply -f secret.yaml

Using Secrets as Environment Variables

yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-secret
spec:
  containers:
  - name: app
    image: nginx
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: password
    # Import all keys
    envFrom:
    - secretRef:
        name: db-secret

Using Secrets as Volume Mounts

yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-secret-volume
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-secret
      defaultMode: 0400  # Read-only for owner

When mounted as a volume, each key becomes a file containing the decoded secret value.

Secret Types

TypePurpose
OpaqueGeneric key-value secrets (default)
kubernetes.io/service-account-tokenService account credentials
kubernetes.io/dockercfgDocker registry credentials (legacy)
kubernetes.io/dockerconfigjsonDocker registry credentials
kubernetes.io/basic-authBasic authentication credentials
kubernetes.io/ssh-authSSH authentication keys
kubernetes.io/tlsTLS certificates
bootstrap.kubernetes.io/tokenBootstrap tokens

Secret Immutability

yaml
apiVersion: v1
kind: Secret
metadata:
  name: immutable-secret
immutable: true
type: Opaque
data:
  api-key: c3VwZXJzZWNyZXQ=

Viewing Secrets

bash
# List secrets
kubectl get secrets

# Describe secret (does not show values)
kubectl describe secret db-secret

# Get secret with decoded values
kubectl get secret db-secret -o yaml

# Decode a specific value
kubectl get secret db-secret -o jsonpath="{.data.password}" | base64 -d

Part 4: ConfigMap vs Secret Comparison

AspectConfigMapSecret
PurposeNon-sensitive configurationSensitive information
EncodingPlain textBase64 encoded
Size limit1MB1MB
etcd encryptionNoOptional (enable with flag)
RBAC separationStandardMore restrictive default
Use casesEnvironment config, feature flagsPasswords, API keys, tokens

Part 5: Environment Variables from ConfigMaps and Secrets

Complete Example: Pod with Mixed Configuration Sources

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-config
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    # Hardcoded
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    # From ConfigMap individual keys
    - name: APP_ENV
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV
    # From Secret individual keys
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: password
    # Import all ConfigMap keys
    envFrom:
    - configMapRef:
        name: app-config
    # Import all Secret keys
    - secretRef:
        name: db-secret

Updating Configuration

When you update a ConfigMap or Secret, changes are not automatically reflected in running Pods.

Options for updating:

  1. Delete and recreate Pods: Kubernetes eventually restarts them

  2. Use immutable ConfigMaps: Force deployment change

  3. Use a sidecar reloader: Tools like Reloader watch for changes

  4. Mount as volume: Changes appear eventually (may have delay)

yaml
# Using Reloader annotation
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  annotations:
    reloader.stakater.com/auto: "true"
spec:
  ...

Real-World Scenarios

Scenario 1: Web Application Configuration

A web application needs different configuration for dev, staging, and production.

yaml
# config-dev.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: dev
data:
  API_URL: https://dev-api.example.com
  FEATURE_FLAG_X: "true"
  LOG_LEVEL: debug
---
# config-prod.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: prod
data:
  API_URL: https://api.example.com
  FEATURE_FLAG_X: "false"
  LOG_LEVEL: warn

Scenario 2: Database Credentials Management

Managing database credentials securely.

bash
# Create secrets for each environment
kubectl create secret generic db-secret \
  --namespace=prod \
  --from-literal=username=prod_user \
  --from-literal=password=$(openssl rand -base64 32)

kubectl create secret generic db-secret \
  --namespace=staging \
  --from-literal=username=staging_user \
  --from-literal=password=$(openssl rand -base64 32)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  namespace: prod
spec:
  template:
    spec:
      containers:
      - name: api
        image: myapi:latest
        env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password

Scenario 3: TLS Certificate for Ingress

yaml
# Create TLS secret
kubectl create secret tls example-tls \
  --cert=example.crt \
  --key=example.key
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  tls:
  - hosts:
    - example.com
    secretName: example-tls
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

Scenario 4: Application Properties File

An application expects a application.properties file.

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-properties
data:
  application.properties: |
    server.port=8080
    spring.datasource.url=jdbc:postgresql://postgres:5432/mydb
    spring.datasource.username=${DB_USERNAME}
    logging.level.com.myapp=DEBUG
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: spring-app:latest
        env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: username
        volumeMounts:
        - name: config
          mountPath: /app/config
      volumes:
      - name: config
        configMap:
          name: app-properties

Configuration Best Practices

Do's

  • Use ConfigMaps for environment-specific config: Different ConfigMaps per environment

  • Use Secrets for sensitive data: Never put passwords in ConfigMaps

  • Use envFrom sparingly: Explicit keys are easier to track

  • Version your ConfigMaps: Use annotations or naming conventions

  • Set immutable when configuration is stable: Prevents accidental changes

  • Use RBAC to restrict Secret access: Only necessary Pods should access Secrets

  • Encrypt Secrets at rest: Enable etcd encryption in production

Don'ts

  • Never commit Secrets to Git: Use Sealed Secrets or external vault

  • Don't use environment variables for large config: Use volume mounts instead

  • Don't embed configuration in images: Separate config from code

  • Don't share Secrets across namespaces: Create Secrets per namespace

Sealed Secrets for Git

Store encrypted Secrets in Git:

bash
# Install kubeseal
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.5/kubeseal-linux-amd64 -O kubeseal

# Create a regular Secret
kubectl create secret generic db-secret --dry-run=client --from-literal=password=secret -o yaml > secret.yaml

# Seal it
kubeseal < secret.yaml > sealed-secret.yaml

# Commit sealed-secret.yaml to Git

Summary

ObjectPurposeSensitive
ConfigMapNon-sensitive configurationNo
SecretSensitive informationYes
Environment VariablesRuntime configEither

Delivery Methods

MethodUse CaseUpdate Behavior
Environment variablesSimple values, flagsPod restart required
Volume mountsFiles, complex configAuto-update (with delay)
Command-line argumentsStartup parametersPod restart required

Practice Questions

  1. What is the difference between a ConfigMap and a Secret?

  2. How do you pass environment variables from a ConfigMap to a container?

  3. Why are Secret values base64 encoded, not encrypted?

  4. How can you make a ConfigMap immutable?

  5. What happens to running Pods when you update a ConfigMap?


Learn More

Practice Kubernetes configuration management with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/

Comments

Popular posts from this blog

📊 Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd

  Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd Monitoring and logging are essential for maintaining a healthy and well-performing Kubernetes cluster. In this guide, we’ll cover why monitoring is important, key monitoring tools like Prometheus and Grafana, and logging tools like Fluentd to help you gain visibility into your cluster’s performance and logs. Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🚀 Introduction In today’s fast-paced cloud-native environment, Kubernetes has emerged as the de-facto container orchestration platform. But deploying and managing applications in Kubernetes is just half the ba...

How to Use SKY TTS: The Complete, Step-by-Step Guide for 2025

 What is SKY TTS? SKY TTS  is a free, next-generation  AI audio creation platform  that brings together high-quality  Text-to-Speech ,  Speech-to-Text , and a full suite of professional  audio editing tools  in one seamless experience. Our vision is simple — to make advanced audio technology  free, accessible, and effortless  for everyone. From creators and educators to podcasters, developers, and businesses, SKY TTS helps users produce  studio-grade voice content  without expensive software or technical skills. With support for  70+ languages, natural voices, audio enhancement, waveform generation, and batch automation , SKY TTS has become a trusted all-in-one toolkit for modern digital audio workflows. Why Choose SKY TTS? Instant Conversion:  Enjoy rapid text-to-speech generation, even with large documents. Advanced Voice Settings:   Adjust speed, pitch, and style for a personalized listening experience. Multi-...

Introduction to Terraform – The Future of Infrastructure as Code

  Introduction to Terraform – The Future of Infrastructure as Code In today’s fast-paced DevOps world, managing infrastructure manually is outdated . This is where Terraform comes in—a powerful Infrastructure as Code (IaC) tool that allows you to define, provision, and manage cloud infrastructure efficiently . Whether you're working with AWS, Azure, Google Cloud, or on-premises servers , Terraform provides a declarative, automation-first approach to infrastructure deployment. Shape Your Future with AI & Infinite Knowledge...!! Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! In today’s digital-first world, agility and automation are no longer optional—they’re essential. Companies across the globe are rapidly shifting their operations to the cloud to keep up with the pace of innovatio...