Skip to content
Laviyx Learn

Try

Docker and Kubernetes course menu

Kubernetes ConfigMaps, Secrets and Health Probes

Configure Kubernetes apps with ConfigMaps and Secrets, keep data with persistent volumes, and add liveness and readiness probes. Then see where Helm fits.

Advanced11 min readUpdated
On this page

ConfigMaps#

A ConfigMap stores settings outside the image, so that the same image works in every environment:

Terminal
kubectl create configmap app-config --from-literal=APP_ENV=staging --from-literal=LOG_LEVEL=debug
kubectl get configmap app-config -o jsonpath='{.data}{"\n"}'
Output
configmap/app-config created
{"APP_ENV":"staging","LOG_LEVEL":"debug"}

Secrets#

A Secret is similar, but for sensitive values:

Terminal
kubectl create secret generic db-secret --from-literal=password='s3cret'
kubectl get secret db-secret -o jsonpath='{.data.password}{"\n"}'
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
echo
Output
secret/db-secret created
czNjcmV0
s3cret

Use them in a pod#

Terminal
cat > app.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  restartPolicy: Never
  containers:
    - name: app
      image: alpine:3.24
      command: ["sh", "-c", "echo $APP_ENV $LOG_LEVEL; echo password has ${#DB_PASSWORD} characters; cat /etc/app/LOG_LEVEL; echo"]
      envFrom:
        - configMapRef:
            name: app-config
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password
      volumeMounts:
        - name: config
          mountPath: /etc/app
  volumes:
    - name: config
      configMap:
        name: app-config
EOF
kubectl apply -f app.yaml
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/app --timeout=60s > /dev/null
kubectl logs app
kubectl delete pod app --wait=false > /dev/null
Output
pod/app created
staging debug
password has 6 characters
debug

The values can arrive as environment variables, or as files. Files update while a pod runs, and environment variables need a restart.

Probes#

Kubernetes checks your container with probes.

ProbeIf it fails
ReadinessThe pod gets no traffic until it passes
LivenessThe container is restarted
StartupGives slow apps time before the others begin
Terminal
cat > probes.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: probed
spec:
  replicas: 1
  selector:
    matchLabels:
      app: probed
  template:
    metadata:
      labels:
        app: probed
    spec:
      containers:
        - name: web
          image: nginx:1.30-alpine
          readinessProbe:
            httpGet:
              path: /
              port: 80
            periodSeconds: 2
          livenessProbe:
            httpGet:
              path: /
              port: 80
            periodSeconds: 2
            failureThreshold: 2
EOF
kubectl apply -f probes.yaml
kubectl rollout status deployment/probed --timeout=90s | tail -n 1
pod=$(kubectl get pods -l app=probed -o name)
kubectl get "$pod" -o jsonpath='restarts={.status.containerStatuses[0].restartCount}{"\n"}'
Output
deployment.apps/probed created
deployment "probed" successfully rolled out
restarts=0

Now we break the app, by deleting its home page. The liveness probe fails, and Kubernetes restarts the container, which brings the file back from the image:

Terminal
kubectl exec "$pod" -- rm /usr/share/nginx/html/index.html
sleep 15
kubectl get "$pod" -o jsonpath='restarts={.status.containerStatuses[0].restartCount}{"\n"}'
kubectl describe "$pod" | grep -m1 -o 'Container web failed liveness probe, will be restarted'
kubectl delete deployment probed > /dev/null
Output
restarts=1
Container web failed liveness probe, will be restarted

Storing data#

Pods are disposable, so data needs a PersistentVolumeClaim (PVC), a request for storage:

Terminal
cat > pvc.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 100Mi
---
apiVersion: v1
kind: Pod
metadata:
  name: writer
spec:
  containers:
    - name: c
      image: alpine:3.24
      command: ["sh", "-c", "echo saved > /data/file; sleep 60"]
      volumeMounts:
        - name: store
          mountPath: /data
  volumes:
    - name: store
      persistentVolumeClaim:
        claimName: data
EOF
kubectl apply -f pvc.yaml
kubectl wait --for=condition=Ready pod/writer --timeout=60s > /dev/null
kubectl exec writer -- cat /data/file
kubectl get pvc data --no-headers | awk '{print $1, $2}'
kubectl delete -f pvc.yaml --wait=false > /dev/null
kubectl delete configmap app-config > /dev/null
kubectl delete secret db-secret > /dev/null
Output
persistentvolumeclaim/data created
pod/writer created
saved
data Bound

For databases, a StatefulSet gives each pod a stable name and its own storage. Many teams use a managed database service instead.

Helm#

Real apps need many YAML files. Helm is a package manager for Kubernetes. A chart bundles those files with settings:

Terminal
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-db bitnami/postgresql --set auth.database=shop
helm upgrade my-db bitnami/postgresql
helm uninstall my-db

Kustomize, built into kubectl, is another way to manage environments.

Where to go next#

  • Run a cluster on a cloud provider: EKS, GKE or AKS.
  • Learn RBAC, to control who can do what.
  • Look at GitOps tools such as Argo CD, which deploy from Git.
  • Consider the CKA or CKAD certifications.

Key takeaway#

Next: Project: Notes App with Compose.

Finished this lesson?

Some links are affiliate links. If you buy through them, Laviyx Learn may earn a commission at no extra cost to you. As an Amazon Associate, we earn from qualifying purchases.