Introduction
Applications often need values that should not be placed directly in a container image or a plain manifest, such as passwords, tokens, or database credentials. Kubernetes Secrets store this kind of sensitive configuration as a separate object and let Pods consume it when they start.
In this lab, you will create a Secret, inspect its encoded data carefully, connect it to an application Pod, update the Secret, and recreate the Pod so the application reads the new values. This lab focuses on the core Secret workflow. Later labs will practice more detailed environment variable wiring and file-based configuration mounts.
Prepare a Secret Practice Namespace
In this step, you will prepare a clean namespace for Secret practice.
Start in /home/labex/project. You will write one Pod manifest in this directory.
mkdir -p /home/labex/project
cd /home/labex/project
Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.
kubectl get nodes --request-timeout=5s
Expected output includes:
NAME STATUS ROLES AGE VERSION
labex-v135 Ready control-plane ... v1.35...
Wait until the cluster networking Pod is Ready before creating application objects.
kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s
Expected output:
pod/calico-node-... condition met
Create a namespace named ckad-prep-secrets:
kubectl create namespace ckad-prep-secrets
Set this namespace as the current context namespace:
kubectl config set-context --current --namespace=ckad-prep-secrets
Confirm the current namespace:
kubectl config view --minify --output 'jsonpath={..namespace}'; echo
Expected output:
ckad-prep-secrets
Create and Inspect a Secret
In this step, you will create a Secret that stores database connection settings for a small application.
The kubectl create secret generic command creates an opaque Secret. generic means Kubernetes does not assign a special built-in meaning to the keys. Each --from-literal flag adds one key-value pair to the Secret.
kubectl create secret generic db-credentials \
--from-literal=DB_USER=orders-app \
--from-literal=DB_PASSWORD=dev-password-123 \
--from-literal=DB_NAME=orders
Expected output:
secret/db-credentials created
Inspect the Secret summary:
kubectl get secret db-credentials
Expected output:
NAME TYPE DATA AGE
db-credentials Opaque 3 ...
View the Secret as YAML. Kubernetes stores the values under data as base64-encoded strings, so you should not expect to see the original password in this output.
kubectl get secret db-credentials -o yaml
Expected output includes:
data:
DB_NAME: b3JkZXJz
DB_PASSWORD: ...
DB_USER: b3JkZXJzLWFwcA==
type: Opaque
Base64 encoding is not encryption. Anyone who can read the Secret object can decode its values. Decode only the username here so you can see how the representation works without printing the password.
kubectl get secret db-credentials -o 'jsonpath={.data.DB_USER}' | base64 --decode; echo
Expected output:
orders-app
Configure a Pod from the Secret
In this step, you will start a Pod that reads the Secret as environment variables.
The envFrom.secretRef field imports all keys from a Secret into the container environment. This is similar to the ConfigMap pattern from the previous lab, but the values come from a Secret object. The command in this Pod prints the username and database name, then only confirms that a password was loaded. It does not print the password itself.
Create secret-reader-pod.yaml:
cat > secret-reader-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: secret-reader
labels:
app: secret-reader
spec:
terminationGracePeriodSeconds: 0
containers:
- name: reader
image: busybox:1.36
command:
- /bin/sh
- -c
- 'echo user=${DB_USER}; echo database=${DB_NAME}; if [ -n "${DB_PASSWORD}" ]; then echo password-loaded; fi; sleep 3600'
envFrom:
- secretRef:
name: db-credentials
EOF
Apply the Pod manifest:
kubectl apply -f secret-reader-pod.yaml
Expected output:
pod/secret-reader created
Wait until the Pod is Ready:
kubectl wait --for=condition=Ready pod/secret-reader --timeout=120s
Expected output:
pod/secret-reader condition met
Read the Pod logs. The application confirms that the Secret values were available, but the password value is not shown.
kubectl logs secret-reader -c reader
Expected output:
user=orders-app
database=orders
password-loaded
Update the Secret and Recreate the Pod
In this step, you will update the Secret and recreate the Pod so the container receives the new values.
A container environment is captured when the container starts. If a Secret is consumed as environment variables, updating the Secret object does not rewrite the environment inside an already running container. Start a new Pod when the application must read updated Secret values.
Patch the Secret with a new application user and password:
kubectl patch secret db-credentials --type merge --patch '{"stringData":{"DB_USER":"orders-worker","DB_PASSWORD":"rotated-password-456","DB_NAME":"orders"}}'
Expected output:
secret/db-credentials patched
Confirm the updated username by decoding only DB_USER:
kubectl get secret db-credentials -o 'jsonpath={.data.DB_USER}' | base64 --decode; echo
Expected output:
orders-worker
Delete the old Pod:
kubectl delete pod secret-reader
Expected output includes:
pod "secret-reader" deleted
Recreate the Pod from the same manifest and wait until it is Ready:
kubectl apply -f secret-reader-pod.yaml
kubectl wait --for=condition=Ready pod/secret-reader --timeout=120s
Expected output:
pod/secret-reader created
pod/secret-reader condition met
Read the logs again. The new Pod now sees the updated user from the Secret and still avoids printing the raw password.
kubectl logs secret-reader -c reader
Expected output:
user=orders-worker
database=orders
password-loaded
Summary
You created a Kubernetes Secret, inspected its encoded data, decoded a non-sensitive key to understand the representation, connected the Secret to an application Pod, and recreated the Pod after updating the Secret. You also practiced an important habit for sensitive configuration: verify that the application received the password without printing the password value into logs.


