Home › CKAD › Topics › Practice tasks › ConfigMaps & Secrets
CKAD · Application Environment, Configuration and Security

Move Hard-Coded Credentials into a Kubernetes Secret

In namespace default, Deployment api-server exists with hard-coded environment variables: DB_USER=admin DB_PASS=Secret123!

Check live lab access → All CKAD tasks

Live lab access

This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.

Create an account to continue →

Read this week's free task →

On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.

The task

Task

In namespace default, Deployment api-server exists with hard-coded environment variables:

Requirements

  1. Create a Secret named db-credentials in namespace default containing these credentials
  2. Update Deployment api-server to use the Secret via valueFrom.secretKeyRef
  3. Do not change the Deployment name or namespace

Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster.

Move credentials out of a manifest and into a Secret, three ways

A Secret is a namespaced object that holds small pieces of confidential data as key/value pairs. Pods consume it in three ways: one key per environment variable (valueFrom.secretKeyRef), every key at once (envFrom), or as files in a volume. Picking the right one, and proving that no plain-text value is left in the workload spec, is the core CKAD skill here. Keep one fact in mind throughout: the values are only base64-encoded, which is an encoding anyone can reverse, not encryption.

Secret quote-api-auth {client-id, client-secret}
   ├─ env[].valueFrom.secretKeyRef ──→ one variable per key, any name you choose
   ├─ envFrom[].secretRef          ──→ all keys, key names become variable names
   └─ volumes[].secret             ──→ one file per key under the mountPath

1. Create the Secret and see what "encoded" means

kubectl create namespace prepium-tutorial
kubectl -n prepium-tutorial create secret generic quote-api-auth \
  --from-literal=client-id='reports' \
  --from-literal=client-secret='Qx7$pl!m'
kubectl -n prepium-tutorial get secret quote-api-auth -o yaml
kubectl -n prepium-tutorial get secret quote-api-auth \
  -o jsonpath='{.data.client-secret}' | base64 -d

The single quotes stop your shell from expanding $ and !. Without them the stored value silently differs from what you typed. generic creates the default type, Opaque; other built-in types such as kubernetes.io/tls or kubernetes.io/dockerconfigjson enforce specific keys. The last command prints the clear value, which shows why Secrets are protected by RBAC and, on real clusters, encryption at rest: by default they are stored unencrypted in etcd.

2. Reference single keys as environment variables

Save this as ckad-secret-deploy.yaml. The env entries use secretKeyRef, the pattern to choose when the application expects particular variable names or needs only some keys. It also lets you rename: the key client-secret becomes QUOTE_CLIENT_SECRET.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: quote-api
  namespace: prepium-tutorial
spec:
  replicas: 1
  selector:
    matchLabels:
      app: quote-api
  template:
    metadata:
      labels:
        app: quote-api
    spec:
      containers:
        - name: api
          image: busybox:1.36
          command: ["sh", "-c", "sleep 3600"]
          env:
            - name: QUOTE_CLIENT_ID          # renamed: the key is client-id
              valueFrom:
                secretKeyRef:
                  name: quote-api-auth
                  key: client-id
            - name: QUOTE_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: quote-api-auth
                  key: client-secret
          volumeMounts:
            - name: auth
              mountPath: /etc/quote-auth
              readOnly: true
      volumes:
        - name: auth
          secret:
            secretName: quote-api-auth
kubectl apply -f ckad-secret-deploy.yaml
kubectl -n prepium-tutorial rollout status deployment/quote-api

The alternative, envFrom with a secretRef, injects every key under its own key name (an optional prefix is prepended). It is shorter, but you lose control over names and pull in keys the app may not need.

3. Mount the same Secret as files

The volumes and volumeMounts part of the same manifest exposes each key as a file named after the key under /etc/quote-auth, mounted read-only. Mounted files are refreshed in place, eventually, when the Secret changes (unless mounted with subPath). Environment variables are not: a container sees new values only after it restarts.

4. Verify from inside and from the spec

kubectl -n prepium-tutorial exec deploy/quote-api -- printenv QUOTE_CLIENT_ID
kubectl -n prepium-tutorial exec deploy/quote-api -- ls /etc/quote-auth
kubectl -n prepium-tutorial get deploy quote-api \
  -o jsonpath='{.spec.template.spec.containers[0].env}'

The first two prove the data arrives. The third proves the template contains only references and no value: fields holding the secret itself, which is the point of the exercise.

Common mistakes

Example checkEvidence
Secret holds the right dataDecoded keys match the intended values
Workload references, not copiesEnv entries use valueFrom.secretKeyRef with correct name and key
Values reach the processprintenv inside the new Pod
Rollout finishedNew Pods are Ready after the template change

Cleanup: kubectl delete namespace prepium-tutorial. References: Secrets, Distribute Credentials Securely Using Secrets, kubectl create secret generic. Next, try TLS Secrets or security contexts.

Exam
CKAD
Domain
Application Environment, Configuration and Security
Grading
Programmatic · partial credit

What this tests

Wire up configmaps, secrets, service accounts, RBAC, securityContext, and resource limits. On the CKAD exam, Application Environment, Configuration and Security tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.

Practice it for real

The public description lets you study the task before signing up. Full access includes this live lab; the selected Task of the Week is free with an account. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.

Related CKAD tasks