E-NO
Kubernetes Secrets architecture 7 Min Read

Kubernetes Secrets Architecture Explained with Practical Examples

calendar_today Published: 2026-08-26
update Last Updated: 2026-08-26
analytics SEO Efficiency: 100%
Technical guide illustration for Kubernetes Secrets Architecture Explained with Practical Examples.

Intro

Kubernetes Secrets hold credentials and other sensitive configuration so your workloads can access databases, APIs, and services without baking secrets into images or source control. This guide explains the architecture behind Secrets, shows how data flows through the cluster, and gives practical, verified procedures for configuring, diagnosing, and recovering from failures. The emphasis is operational safety: observe first, change the smallest piece possible, verify the result, and document how to undo it.

Architecture at a Glance

  • Components involved
  • kube-apiserver: validates and stores Secret objects in etcd.
  • etcd: persists Secrets; values are base64-encoded, not encrypted by default. Enable encryption at rest for protection.
  • Admission/RBAC: control who can create, update, get, list, or watch Secrets.
  • kubelet: fetches Secrets referenced by Pods scheduled to its node and makes them available as environment variables or volumes.
  • Controllers: rollouts and restarts when Pod specs change.
  • Data flow
  1. You create a Secret via kubectl or a controller (GitOps, operator).
  2. kube-apiserver validates and writes it to etcd.
  3. When a Pod references the Secret, the kubelet on that node retrieves it over a secure channel.
  4. The Secret is exposed to the container as env vars or mounted files.
  • Security notes
  • In transit: TLS between components.
  • At rest: enable encryption-at-rest for etcd; otherwise values are only base64-encoded.
  • Access control: strictly scope RBAC to limit which subjects can read Secrets.

Version and Environment Inventory

Before changing anything, inventory your environment and capture the current state. The examples below work with Kubernetes 1.23–1.29 and kubectl.

  • Confirm cluster and client versions
kubectl version --short
kubectl config current-context
kubectl get ns
  • Pick a namespace and set it for subsequent commands
NAMESPACE=secrets-lab
kubectl create namespace "$NAMESPACE" 2>/dev/null || true
kubectl config set-context --current --namespace="$NAMESPACE"
  • Read-only observation of Secrets and Pods
kubectl get secrets
kubectl get pods -o wide
  • Check rollout health before assuming a deployment succeeded
kubectl rollout status deployment/<name>
  • Optional (self-managed control plane): verify encryption at rest
  • Look for the kube-apiserver flag --encryption-provider-config and a valid configuration file on the control-plane node.
  • On managed clusters, consult your provider’s documentation for enabling and verifying encryption at rest.

Keep the initial test scope small: apply one manifest, observe the generated resources, and verify from a single Pod before introducing ingress or load balancers.

Quick check 1 of 2

What is the default storage format for secret values in etcd according to the article, and what can be enabled for protection?

The Architecture at a Glance section states etcd persists Secrets with values that are base64-encoded, not encrypted by default, and advises enabling encryption at rest for protection.

Safe Configuration Path (Smallest Justified Change)

We will create a minimal Secret, consume it via both env vars and a volume, and verify from inside a pod. Use placeholder values—never real credentials—during dry runs.

  1. Create a Secret from literal values
cat <<'EOF' > secret-db.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  labels:
    app: demo
  # Set immutable: true after you are confident with keys and names
# immutable: true
type: Opaque
stringData:
  username: demo_user
  password: demo_password
  url: postgres://db:5432/app
EOF

kubectl apply -f secret-db.yaml
kubectl get secret db-credentials -o yaml

Expect to see data keys (base64-encoded) or stringData keys (if the API server is echoing the request back; stored form is data).

  1. Deploy a pod that consumes the Secret

This example uses a simple container that sleeps so you can exec into it.

cat <<'EOF' > deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
      # Update this checksum when rotating secrets to force a rollout
      annotations:
        secret-checksum: "$(date +%s)"
    spec:
      containers:
      - name: app
        image: alpine:3.19
        command: ["/bin/sh","-c","sleep 3600"]
        env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
        volumeMounts:
        - name: db-secret
          mountPath: /etc/db
          readOnly: true
      volumes:
      - name: db-secret
        secret:
          secretName: db-credentials
EOF

kubectl apply -f deploy.yaml
kubectl rollout status deployment/demo-app
  1. Verify from inside the Pod
POD=$(kubectl get pod -l app=demo -o jsonpath='{.items[0].metadata.name}')
# Check env vars
kubectl exec "$POD" -- sh -c 'echo "$DB_USER" && echo "$DB_URL"'
# Check mounted files
kubectl exec "$POD" -- ls -l /etc/db
kubectl exec "$POD" -- sh -c 'printf "username: "; cat /etc/db/username; echo; printf "url: "; cat /etc/db/url; echo'

If a key name is wrong, the container may start with an empty env var or the volume may mount without the missing key. We will cover diagnosis next.

Verification and Diagnostics

  • Inspect Secret contents safely (local decode only)
kubectl get secret db-credentials -o jsonpath='{.data.username}' | base64 -d; echo
kubectl get secret db-credentials -o jsonpath='{.data.url}' | base64 -d; echo
  • Pod-level troubleshooting
kubectl get pods -o wide
kubectl describe pod "$POD"

Look for events such as:

  • CreateContainerConfigError: secret "db-credentials" not found
  • Failed to pull image: unrelated, fix images first
  • Back-off restarting failed container: check the app logs
  • Logs and previous attempt
kubectl logs "$POD" --previous 2>/dev/null || true
  • Rollout signals
kubectl rollout status deployment/demo-app
kubectl describe deployment demo-app | sed -n '/Events/,$p'
  • Confirm that updates take effect
  • Environment variables are captured at container start. After changing a Secret, restart Pods or trigger a rollout to see new values.
  • Mounted Secret volumes update within minutes without a restart (kubelet refresh). Verify by reading the files again from the running Pod.

Failure Modes and Recovery

Common issues and concrete steps to resolve them:

  1. Secret not found
  • Symptom: Pod event shows CreateContainerConfigError: secret "..." not found.
  • Check: kubectl get secret <name> in the Pod’s namespace.
  • Fix: create the Secret or update the reference name; reapply Deployment.
  1. Wrong key name
  • Symptom: env var is empty or file missing under mount path.
  • Check: list keys in the Secret with kubectl get secret <name> -o yaml.
  • Fix: correct key names in the Pod spec or add the missing key to the Secret.
  1. Rotation did not propagate
  • Symptom: Pod still uses old credentials.
  • Reason: env vars require a restart; mounted files may lag briefly.
  • Fix: trigger a Deployment rollout after updating the Secret:
kubectl apply -f secret-db.yaml
kubectl annotate deployment demo-app secret-checksum=$(date +%s) --overwrite
kubectl rollout status deployment/demo-app
  1. Secret too large
  • Symptom: API error about object size limit.
  • Context: Kubernetes object size is capped (around 1 MiB). Large cert bundles or keys can hit this.
  • Fix: store large material externally (object storage or external secret manager) and mount via CSI or init container.
  1. Accidental mutation and drift
  • Symptom: difficult-to-track outage after someone edited a Secret inline.
  • Prevention: set immutable: true once keys are correct. To update, delete and recreate the Secret.
  • Recovery: restore from a known-good manifest or backup:
kubectl get secret db-credentials -o yaml > backup-secret.yaml   # do this before changes
# If broken, roll back:
kubectl apply -f backup-secret.yaml
kubectl rollout status deployment/demo-app
  1. Excessive access to Secrets
  • Symptom: developers or service accounts can list all Secrets.
  • Fix: tighten RBAC. Example: allow only get on a specific secret to a service account:
cat <<'EOF' | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: read-db-secret
  namespace: secrets-lab
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-credentials"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: bind-read-db-secret
  namespace: secrets-lab
subjects:
- kind: ServiceAccount
  name: default
  namespace: secrets-lab
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: read-db-secret
EOF

Quick check 2 of 2

After updating a Secret that is used as an environment variable in a pod, what is required for the pod to see the new value?

The Verification and Diagnostics section states environment variables are captured at container start, so after changing a Secret you must restart Pods or trigger a rollout to see new values.

Operations Checklist (Practical and Auditable)

Use this before and after each change:

  • Observe first
  • Record cluster version, context, namespace, and timestamps.
  • kubectl get secrets, kubectl describe pods, and kubectl rollout status to establish baseline.
  • Scope and protect
  • Use placeholder values in dev; never paste real credentials into terminals recorded by session tools.
  • Keep each change minimal: one Secret or one Deployment at a time.
  • Store and ship
  • Manage Secrets as code using stringData in manifests kept in a private repo plus a sealing/templating mechanism (e.g., sealed-secrets or an operator) so plaintext never lands in Git history.
  • Label Secrets (owner, app, rotation date) for discoverability.
  • Access control and encryption
  • Enforce least-privilege RBAC: avoid cluster-wide list on secrets.
  • Enable encryption at rest for etcd.
  • Restrict who can get or watch Secrets via Admission and RBAC policies.
  • Consumption patterns
  • Prefer mounted files for values that change without restarts; use env vars only for fixed-at-start values.
  • Add a checksum annotation to Deployments that consume Secrets via env to force deterministic rollouts on rotation.
  • Rotation and recovery
  • Rotate credentials on a schedule; script updates and rollouts.
  • Keep a tested rollback: a previous Secret manifest and kubectl rollout undo for Deployments.
  • Audit and visibility
  • Enable audit logs on the API server (self-managed) or the provider’s equivalent.
  • Periodically query which Pods reference each Secret.
kubectl get pods -A -o json | jq -r '
  .items[] | {ns:.metadata.namespace, name:.metadata.name, secrets:(.spec.volumes // []) + [] | map(select(.secret!=null) | .secret.secretName)}
'

Beyond Built-ins: External Managers (Optional)

For organizations standardizing on cloud or on-prem KMS/secret managers, integrate with Kubernetes using the Secrets Store CSI Driver or an operator that syncs external secrets to native Secret objects. Typical workflow:

  • Define a reference (e.g., SecretProviderClass) to a path/key in the external store.
  • Mount the material as a volume in Pods or sync into a Kubernetes Secret.
  • Verify with the same pod-exec checks and monitor rotation events from the external system.

Keep the same safety principles: start in a test namespace, observe, change one thing, and confirm mount contents before rolling out broadly.

Conclusion

Kubernetes Secrets provide a clean, auditable path to deliver sensitive configuration to workloads, but operational safety depends on understanding how Secrets move through the system. Start with a small, version-scoped test: create a Secret, reference it from a Deployment, verify env vars and mounted files, and watch rollout status. Diagnose with describe, logs, and jsonpath decodes; recover with backups and controlled rollouts. Enforce RBAC and enable encryption at rest, prefer volumes for values that change, and use annotations to trigger deterministic restarts when rotating credentials. With these concrete steps, you can ship changes confidently while keeping exposure and blast radius small.

Related Research

Article Quality Score

Reader usefulness 100%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL