Configure Applications with Secrets

KubernetesBeginner
Practice Now

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.