ServerlessBase Blog
  • Introduction to Kubernetes ConfigMaps and Secrets

    A practical guide to managing configuration and sensitive data in Kubernetes applications

    Introduction to Kubernetes ConfigMaps and Secrets

    You've probably deployed a containerized application to Kubernetes and immediately hit a wall: how do you manage configuration values without hardcoding them into your Docker images? How do you handle sensitive data like API keys and database passwords securely? If you're struggling with environment variables scattered across multiple files or secrets committed to version control, you're not alone. This is where Kubernetes ConfigMaps and Secrets come in.

    In this guide, you'll learn how to separate configuration from container images, manage non-sensitive configuration with ConfigMaps, and handle sensitive data securely with Secrets. You'll see practical examples of how to use these resources in real deployments, understand the key differences between them, and learn best practices that will keep your applications maintainable and secure.

    What Are ConfigMaps and Secrets?

    Think of ConfigMaps as a key-value store for non-sensitive configuration data. They allow you to externalize configuration from your container images, making your deployments more flexible and easier to manage. When you need to change a setting—like a database URL, feature flags, or application thresholds—you can update the ConfigMap without rebuilding your image.

    Secrets are similar to ConfigMaps but designed specifically for sensitive data. They store passwords, OAuth tokens, SSH keys, and other credentials that shouldn't be exposed in plain text. Kubernetes encrypts Secrets at rest and provides additional security controls to prevent accidental exposure.

    ConfigMaps: Managing Non-Sensitive Configuration

    Basic ConfigMap Structure

    A ConfigMap is a Kubernetes resource that stores non-confidential data in key-value pairs. Here's a simple example:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: app-config
    data:
      database-url: postgresql://user:password@db-service:5432/myapp
      feature-flags: '{"new-ui": true, "beta-analytics": false}'
      log-level: info

    This ConfigMap defines three configuration values that your application can read at runtime. The data section contains the actual key-value pairs, while metadata.name gives the ConfigMap a unique identifier.

    Mounting ConfigMaps as Files

    The most common way to use ConfigMaps is to mount them as files in your container's filesystem. This approach works well for applications that read configuration from files rather than environment variables.

    apiVersion: v1
    kind: Pod
    metadata:
      name: configmap-demo
    spec:
      containers:
      - name: app
        image: myapp:latest
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
      - name: config-volume
        configMap:
          name: app-config

    In this example, the ConfigMap app-config is mounted to /etc/config inside the container. Each key-value pair becomes a separate file, so database-url becomes /etc/config/database-url containing the database connection string.

    Using ConfigMaps as Environment Variables

    Many applications prefer to read configuration from environment variables. Kubernetes makes this easy with the envFrom field:

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-demo
    spec:
      containers:
      - name: app
        image: myapp:latest
        envFrom:
        - configMapRef:
            name: app-config

    This mounts all keys from app-config as environment variables in the container. The database-url key becomes the DATABASE_URL environment variable (Kubernetes automatically converts keys to uppercase and replaces dots with underscores).

    ConfigMap with Multiple Data Sources

    You can define ConfigMaps using different data sources:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: multi-source-config
    data:
      # From literal values
      app-name: "My Application"
      app-version: "1.0.0"
      # From files
      from-file: |
        This is content from a file
        Second line

    The from-file key demonstrates how you can include multi-line configuration directly in the ConfigMap definition.

    Secrets: Handling Sensitive Data Securely

    Basic Secret Structure

    Secrets work similarly to ConfigMaps but are designed for sensitive data. Here's an example:

    apiVersion: v1
    kind: Secret
    metadata:
      name: database-credentials
    type: Opaque
    data:
      username: YWRtaW4=
      password: c2VjcmV0cGFzc3dvcmQ=

    Notice that the values are base64-encoded. This is intentional—Secrets are stored as base64-encoded strings in etcd, not in plain text. However, base64 encoding is not encryption; it's just a way to represent binary data as text.

    Creating Secrets from Strings

    You can create Secrets directly from strings without manual base64 encoding:

    apiVersion: v1
    kind: Secret
    metadata:
      name: api-keys
    type: Opaque
    stringData:
      api-key: "sk-1234567890abcdef"
      oauth-token: "ghp_abcdefghijklmnopqrstuvwxyz123456"

    The stringData field is decoded to base64 when the Secret is created, making it much easier to work with.

    Mounting Secrets as Files

    Like ConfigMaps, Secrets can be mounted as files:

    apiVersion: v1
    kind: Pod
    metadata:
      name: secret-demo
    spec:
      containers:
      - name: app
        image: myapp:latest
        volumeMounts:
        - name: secret-volume
          mountPath: /etc/secrets
      volumes:
      - name: secret-volume
        secret:
          secretName: database-credentials

    The Secret is mounted to /etc/secrets inside the container, with each key becoming a file. The file contents are decoded from base64, so your application receives the actual sensitive data.

    Using Secrets as Environment Variables

    Secrets can also be used as environment variables:

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-secret-demo
    spec:
      containers:
      - name: app
        image: myapp:latest
        env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: database-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: database-credentials
              key: password

    This approach gives you fine-grained control over which secrets are exposed as environment variables.

    Key Differences Between ConfigMaps and Secrets

    FeatureConfigMapSecret
    PurposeNon-sensitive configurationSensitive data
    StorageBase64 encodedBase64 encoded
    EncryptionNoYes (at rest)
    Access controlRBAC appliesRBAC applies
    Mount as filesYesYes
    Use as env varsYesYes
    Image pull secretsNoYes

    The most important distinction is security. Secrets are encrypted at rest in etcd and have additional access controls. ConfigMaps are not encrypted and should never contain sensitive data.

    Practical Example: Deploying an Application with Configuration

    Let's walk through a complete example of deploying an application with both ConfigMaps and Secrets.

    Application Requirements

    Your application needs:

    • A database connection string (sensitive)
    • Feature flags (non-sensitive)
    • Log level configuration (non-sensitive)

    Deployment Manifest

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: app-config
    data:
      feature-flags: '{"new-ui": true, "beta-analytics": false}'
      log-level: info
      app-name: "My Application"
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: app-secrets
    type: Opaque
    stringData:
      database-host: "postgres-service"
      database-port: "5432"
      database-name: "myapp"
      database-user: "appuser"
      database-password: "securepassword123"
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: myapp
      template:
        metadata:
          labels:
            app: myapp
        spec:
          containers:
          - name: app
            image: myapp:1.0.0
            envFrom:
            - configMapRef:
                name: app-config
            env:
            - name: DB_HOST
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database-host
            - name: DB_PORT
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database-port
            - name: DB_NAME
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database-name
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database-user
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database-password
            volumeMounts:
            - name: config-volume
              mountPath: /etc/config
            - name: secret-volume
              mountPath: /etc/secrets
          volumes:
          - name: config-volume
            configMap:
              name: app-config
          - name: secret-volume
            secret:
              secretName: app-secrets

    This deployment uses ConfigMaps for feature flags and log levels, and Secrets for database credentials. The application can read configuration from both mounted files and environment variables.

    Best Practices

    ConfigMap Best Practices

    1. Keep ConfigMaps immutable when possible: Immutable ConfigMaps are more efficient and prevent accidental changes.
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: app-config
    immutable: true
    data:
      app-name: "My Application"
    1. Use ConfigMaps for configuration, not code: ConfigMaps are for runtime configuration, not application code.

    2. Avoid large ConfigMaps: Mounting large ConfigMaps can impact pod startup time.

    Secret Best Practices

    1. Never commit Secrets to version control: Always create Secrets through Kubernetes or use external secret management systems.

    2. Use external secret management: For production environments, consider tools like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault.

    3. Rotate secrets regularly: Implement a rotation strategy for sensitive credentials.

    4. Apply RBAC controls: Restrict access to Secrets using Kubernetes Role-Based Access Control.

    5. Use specific secret types: Kubernetes supports predefined secret types like kubernetes.io/dockerconfigjson for image pull secrets.

    Common Pitfalls

    Pitfall 1: Hardcoding Secrets in Images

    Never bake secrets into your Docker images. If an image is compromised, all secrets are exposed.

    Pitfall 2: Using Secrets in ConfigMaps

    ConfigMaps are not encrypted and should never contain sensitive data. Use Secrets for credentials and tokens.

    Pitfall 3: Forgetting to Restart Pods

    When you update a ConfigMap or Secret, pods don't automatically pick up the changes. You must restart them for the changes to take effect.

    Pitfall 4: Exposing Secrets in Logs

    Be careful not to log secrets in application logs. Kubernetes automatically masks secrets in pod events, but your application code must not print them.

    Conclusion

    ConfigMaps and Secrets are essential tools for managing configuration and sensitive data in Kubernetes applications. ConfigMaps provide a flexible way to externalize non-sensitive configuration, while Secrets offer secure storage for sensitive information. By following best practices and understanding the key differences between these resources, you can build more maintainable and secure deployments.

    The next step is to explore how Kubernetes handles configuration updates and how to implement rolling updates when configuration changes. Platforms like ServerlessBase can automate much of this configuration management, handling ConfigMaps and Secrets securely and efficiently for your deployments.

    Practical Next Steps

    1. Create a ConfigMap for your application's non-sensitive configuration
    2. Create a Secret for any sensitive data like database credentials
    3. Update your deployment to use both resources
    4. Test configuration changes by updating the ConfigMap and restarting pods
    5. Implement secret rotation for production environments

    Leave comment