Configuration and state management
ConfigMaps, Secrets, and persistent storage (PV, PVC, StorageClass) and connecting them to pods.
Configuration and state management
In this section you will learn to separate an application’s configuration and secrets from the code, and to understand how Kubernetes handles persistent storage. You will go through ConfigMaps, Secrets, and the storage concepts PV, PVC, and StorageClass. At the end you will connect environment variables and secrets to a running pod.
1. Why separate configuration from the application?
The same container image is meant to be run in different environments (development, test, production) without recompiling. That is why environment-specific configuration is kept separate from the code:
- ConfigMap – non-secret configuration (settings, addresses, flags).
- Secret – secret data (passwords, API keys, certificates).
This way the same application gets different settings in different environments simply by swapping the ConfigMap or Secret.
2. ConfigMaps
A ConfigMap stores non-secret configuration as key–value pairs. You can create it with a command or from a manifest:
# Yksittäisistä arvoista
kubectl create configmap app-config \
--from-literal=APP_GREETING="Hei ConfigMapista" \
--from-literal=APP_TIER=frontend
# Tiedostosta
kubectl create configmap app-config --from-file=./asetukset.propertiesAs a manifest:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_GREETING: "Hei ConfigMapista"
APP_TIER: frontendA ConfigMap’s values can be connected to a pod in three ways:
- as a single environment variable (
configMapKeyRef), - all keys at once as environment variables (
envFrom), - as files on disk (volume mount).
3. Secrets
A Secret is like a ConfigMap but intended for secret data. The values are stored base64-encoded (note: encoding ≠ encryption).
kubectl create secret generic app-secret \
--from-literal=DB_PASSWORD='s3cret-pw'Common Secret types:
Opaque– free-form key–value (default),kubernetes.io/dockerconfigjson– container registry credentials,kubernetes.io/tls– TLS certificate and key.
⚠️ Security: base64 is not encryption — anyone can decode it. Protect Secrets with RBAC, enable etcd encryption at rest (RKE2 supports this), and do not store secrets in version control in plain text. Use external secret management when needed (e.g. Vault, Sealed Secrets).
Connecting a Secret to a pod works the same way as a ConfigMap: as an environment variable
(secretKeyRef / envFrom) or as a volume.
4. Persistent storage: PV, PVC, and StorageClass
Pods are short-lived: when a Pod is removed, its containers’ file system is gone. Persistent storage decouples the data from the Pod’s lifecycle.
- PersistentVolume (PV) – the actual storage resource in the cluster (a disk, NFS, a cloud volume). A cluster-level object.
- PersistentVolumeClaim (PVC) – a Pod’s request for storage of a certain size and type. A Pod references a PVC, not a PV directly.
- StorageClass – describes how storage is created automatically (dynamic provisioning). When a PVC references a StorageClass, a suitable PV is created automatically.
flowchart LR
POD[Pod] --> PVC[PersistentVolumeClaim]
PVC -->|binds to| PV[PersistentVolume]
SC[StorageClass] -->|provisions| PVKey concepts:
- accessModes –
ReadWriteOnce(one node),ReadOnlyMany,ReadWriteMany. - reclaimPolicy – what happens to the PV when the PVC is deleted (
DeleteorRetain).
💡 In an RKE2/Rancher environment, dynamic provisioning is handled by some StorageClass, e.g. a cloud disk service or
local-path. Check the available classes with the commandkubectl get storageclass.
5. Hands-on: connecting environment variables and secrets to a pod
You will create a ConfigMap and a Secret and connect them to a running pod as environment variables and files. First make sure you have the cluster’s kubeconfig in use (see the exercise in the architecture section).
1. Create and switch to your own namespace:
# Korvaa <oma-nimi> omalla nimelläsi
kubectl create namespace <oma-nimi> --dry-run=client -o yaml | kubectl apply -f -
kubectl config set-context --current --namespace=<oma-nimi>2. Create a ConfigMap and a Secret:
kubectl create configmap app-config \
--from-literal=APP_GREETING="Hei ConfigMapista" \
--from-literal=APP_TIER=frontend
kubectl create secret generic app-secret \
--from-literal=DB_PASSWORD='s3cret-pw'3. Deploy a Deployment that consumes both:
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: config-demo
spec:
replicas: 1
selector:
matchLabels:
app: config-demo
template:
metadata:
labels:
app: config-demo
spec:
containers:
- name: app
image: nginx
env:
- name: APP_GREETING
valueFrom:
configMapKeyRef:
name: app-config
key: APP_GREETING
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
volumeMounts:
- name: config-vol
mountPath: /etc/app-config
readOnly: true
volumes:
- name: config-vol
configMap:
name: app-config
EOF4. Verify that the values are visible in the running pod:
# Ympäristömuuttujat (ConfigMapista ja Secretistä)
kubectl exec deploy/config-demo -- printenv APP_GREETING DB_PASSWORD
# ConfigMap tiedostoina volumessa
kubectl exec deploy/config-demo -- ls /etc/app-config
kubectl exec deploy/config-demo -- cat /etc/app-config/APP_GREETING🔎 Observation: When you update a ConfigMap or Secret, the environment variables do not update in the running pod automatically (they are read at startup). Values mounted as a volume, on the other hand, update after a delay. Changing environment variables requires restarting the pod (
kubectl rollout restart deploy/config-demo).
5. (Optional) Request persistent storage with a PVC:
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
EOF
kubectl get pvc dataIf the cluster has a default StorageClass, the PVC reaches the Bound state. Otherwise it
stays Pending until a suitable StorageClass or PV is available.
6. Clean up the exercise by deleting your namespace:
kubectl delete namespace <oma-nimi>Summary
- A ConfigMap stores non-secret and a Secret stores secret configuration; both are connected to a pod as environment variables or as a volume.
- A Secret is only base64-encoded — protect it with RBAC and etcd encryption.
- Environment variables are read at pod startup; a change requires a restart.
- Persistent storage is built from three parts: PV (the resource), PVC (the request), and StorageClass (dynamic provisioning).
- A Pod always references a PVC, never a PV directly.