Skip to main content
RunBook Academy

KubernetesXXI · SecretsSecrets

Consuming Secrets — env vars, mounted files, image pull credentials

Advanced⏱ ~17 minkubectlkubeadm

What you'll learn

  • Configure env-var and file-mount consumption of Secrets
  • Use imagePullSecrets for private registry credentials
  • Choose the right consumption pattern for a credential class
  • Reason about the security trade-offs of each pattern

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Secrets can be consumed by Pods in three patterns: as environment variables, as mounted files, or as imagePullSecrets for private image registry credentials. Each pattern has security trade-offs; choosing the right one is part of the production discipline. This lesson covers the patterns and the visibility chain for each.

Pattern 1: env vars

spec:
  containers:
  - name: web
    image: web:v1
    env:
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password
    - name: DATABASE_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    envFrom:
    - secretRef:
        name: db-credentials

The container’s process sees DATABASE_PASSWORD=s3cr3t in its environment. The value is set at container start and is frozen until the Pod restarts.

flowchart LR
    A[Secret] -->|secretKeyRef| B[Container env]
    B --> C["/proc/<pid>/environ"]
    C --> D[Application]
    C --> E["Any process in Pod<br/>can read"]

Pattern 2: mounted files

spec:
  containers:
  - name: web
    image: web:v1
    volumeMounts:
    - name: db-credentials
      mountPath: /etc/db
      readOnly: true
  volumes:
  - name: db-credentials
    secret:
      secretName: db-credentials
      defaultMode: 0400

The container sees files at /etc/db/:

/etc/db/username
/etc/db/password

Each file is owned by the user the container runs as (or root if runAsUser is not set) and has the mode defaultMode (default 0644, recommended 0400 for credentials).

flowchart LR
    A[Secret] -->|secret volume| B[Container filesystem]
    B --> C["/etc/db/password"]
    B --> D["/etc/db/username"]
    C --> E[Application reads]

The kubelet projects the Secret as files with the specified mode. The application reads the file directly; the value is not visible to other processes unless they have file-system access.

Secret volume updates

flowchart TB
    A[Secret updated] --> B[Kubelet watches]
    B --> C{Period elapsed?}
    C -->|yes| D["Kubelet updates<br/>mount contents"]
    C -->|no| E[Wait]

Like ConfigMap volumes, Secret volumes are eventually consistent (default syncPeriod ~60s). The application must detect the change and reload.

Optional keys

volumes:
- name: db-credentials
  secret:
    secretName: db-credentials
    optional: true

If the Secret does not exist or the key is missing, the Pod is still scheduled (no blocking). For credentials that are optional, this is correct. For credentials that the Pod cannot function without, set optional: false.

Pattern 3: imagePullSecrets

spec:
  containers:
  - name: web
    image: registry.example.com/team/web:v1
  imagePullSecrets:
  - name: reg-credentials

The kubelet uses the credentials to authenticate to the private registry when pulling the image. The Secret type is kubernetes.io/dockerconfigjson or kubernetes.io/dockerconfig.

flowchart LR
    A[kubelet] -->|imagePullSecrets| B[Registry]
    B -->|auth| C[Pull image]
    C --> D[Start container]

The kubelet pulls the image before starting the container; the credentials are not visible to the container after pull. The image is on the node’s local registry; the container starts with the cached image.

flowchart TB
    A["imagePullSecrets:<br/>reg-credentials"] --> B[kubelet]
    B --> C[Read .dockerconfigjson]
    C --> D[Auth to registry]
    D --> E[Pull image]
    E --> F[Cache on node]
    F --> G["Container starts<br/>no credentials visible"]

Comparing the patterns

PatternVisibilityUpdatesUse case
env vars/proc/<pid>/environFrozen at startConnection strings, simple credentials
Mounted filesFile in container FSEventually consistentTLS keys, certificates, structured credentials
imagePullSecretskubelet onlyN/ARegistry authentication

Security trade-offs

Env vars

  • Visibility: any process in the Pod.
  • Logging: env vars may appear in error logs (e.g., when a library logs the connection string on failure).
  • Core dumps: env vars are part of the process state; a core dump on crash writes the env to a file.
  • Updates: frozen; the Pod must restart.

Mounted files

  • Visibility: any process with file-system read access. File mode 0400 limits this to the container’s user.
  • Logging: the file is not in the process env unless the application loads it; accidental logging is rare.
  • Updates: eventual consistency; the application can reload.
  • Persistence: the file is in tmpfs; it is not on disk. A node reboot clears the file (the kubelet re-mounts from the Secret).

imagePullSecrets

  • Visibility: kubelet only; the container never sees the credentials.
  • Logging: the kubelet logs the registry interaction; the credentials are not in the logs.
  • Updates: the kubelet reads the Secret each time it pulls (which is rare after the first pull per node).

Real-world patterns

Pattern: TLS for an Ingress

apiVersion: v1
kind: Secret
metadata:
  name: web-tls
type: kubernetes.io/tls
data:
  tls.crt: <base64 cert>
  tls.key: <base64 key>
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  tls:
  - hosts: [web.example.com]
    secretName: web-tls
  rules:
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80

The Ingress controller mounts the Secret and serves the TLS material. The Pod never sees the private key.

Pattern: registry credentials

kubectl create secret docker-registry reg-credentials \
  --docker-server=registry.example.com \
  --docker-username=service-account \
  --docker-password=$(vault read -field=password secrets/registry) \
  -n prod

The Secret’s data is sourced from an external secret manager; the Secret in the cluster is a rendering.

Pattern: database credentials

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: app
  password: s3cr3t
  database: app_prod

Consumed as env vars:

env:
- name: DATABASE_URL
  value: "postgres://$(DB_USERNAME):$(DB_PASSWORD)@db/$(DB_NAME)"
- name: DB_USERNAME
  valueFrom:
    secretKeyRef:
      name: db-credentials
      key: username
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-credentials
      key: password
- name: DB_NAME
  valueFrom:
    secretKeyRef:
      name: db-credentials
      key: database

The application reads DATABASE_URL at startup; the individual credentials are in env vars but not in the connection string.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the recommended pattern for mounting a Secret as a TLS certificate in an Ingress?

  2. Q2. imagePullSecrets is mounted into the container's filesystem after the image is pulled.

  3. Q3. Your team mounts a Secret with defaultMode 0644 as a file in a container. The container runs as a non-root user. The container cannot read the Secret file. Diagnose and remediate.

    Secret mounted as /etc/db/password. defaultMode 0644 (world-readable). The container runs as runAsUser 10000. The non-root user cannot read the file.

  4. Q4. Why is mounting a Secret as env vars a security risk, and what is the safer pattern?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Mount as files for credentials the application reads from files. TLS keys, certificate chains, SSH keys.
  • Use env vars for connection strings. A single DATABASE_URL env var is fine; the individual credentials can be in separate env vars.
  • Set defaultMode: 0400 on Secret volumes. The default 0644 is readable by any process in the container.
  • Avoid logging Secrets. A library that logs the connection string on error is leaking the password.
  • Audit imagePullSecrets. A Pod that pulls from a private registry without imagePullSecrets will fail at pull time; verify the Secret exists.

Secrets are the workhorse of Kubernetes credential management. The consumption pattern determines the visibility chain. Operators who choose deliberately have Secrets that work securely.