Externalize Configuration
Move environment-specific content out of the image with chart-managed ConfigMaps and Secrets.
An image should mean the same thing in development and production. Configuration answers a different question: how should that image behave in this environment? Hard-coding banners or tokens into the image forces a rebuild for every environment; ConfigMaps and Secrets let one image adapt at deploy time.
Last updated: 2026-07-20
Who this chapter is for
This chapter is for beginners who can deploy with Helm on Minikube (Chapter 5). You will change runtime files and environment values without rebuilding the nginx image.
Download this chapter's files
Use your browser to download either ivia-chapter-06.tar.gz or ivia-chapter-06.zip. The archive is self-contained; you do not need this tutorial repository.
Open a terminal after the browser download:
cd ~/Downloads
tar -xzf ivia-chapter-06.tar.gz
cd ivia-chapter-06
All remaining relative paths in this chapter start from that extracted directory.
The challenge
Challenge: change the static page's environment banner through a ConfigMap without rebuilding simple-static:local, then mount a dummy Secret as an environment variable.
Prerequisites
- A running Minikube cluster with the extracted static source.
- Helm and kubectl.
- Permission to create a
workdirectory in the extracted package.
Learning goals
After this chapter, you will be able to:
- Distinguish build-time assets from runtime configuration
- Create a ConfigMap through chart values and mount it as a file
- Create a dummy Secret and expose it as an environment variable
- Explain that Kubernetes Secrets are encoded, not encrypted by default
Why ConfigMaps and Secrets
| Mechanism | Use for | Not for | | --- | --- | --- | | Hard-coded in image | Truly fixed assets | Environment banners, URLs, credentials | | ConfigMap | Non-sensitive settings and files | Passwords, API keys, tokens | | Secret | Credentials and tokens (still protect access) | Believing data is encrypted at rest by default |
Trade-off: environment variables are simple for short values; mounted files suit multi-line content and avoid leaking values into process listings as easily. Secrets in Kubernetes are base64-encoded in the API and etcd by default—that is not encryption. Restrict RBAC, prefer external secret managers in production, and never commit real credentials.
Prepare the standalone exercise
Build the packaged source into Minikube and prepare the chart's scheduling label:
minikube image build -t simple-static:local static-app
kubectl label node minikube ivia.ch/nodetype=app --overwrite
kubectl create namespace tutorial \
--dry-run=client -o yaml \
| kubectl apply -f -
Create configuration values
We write a temporary values file so the chapter stays self-contained and you can edit one place.
mkdir -p work
cat > work/simple-app-config.yaml <<'EOF'
components:
frontend:
enabled: true
name: frontend
image: {repository: simple-static, tag: local, pullPolicy: IfNotPresent}
port: 8080
health: /healthz
readinessProbe: |
httpGet:
path: {{ .Component.health }}
port: {{ include "ivia.firstPort" . }}
periodSeconds: 5
livenessProbe: |
httpGet:
path: {{ .Component.health }}
port: {{ include "ivia.firstPort" . }}
periodSeconds: 10
configMaps:
- name: page
data:
environment.txt: "Hello from Minikube configuration!"
configMapVolumes:
- name: page-config
configMapName: '{{ include "ivia.componentName" . }}-page'
mountPath: /usr/share/nginx/html/environment.txt
subPath: environment.txt
readOnly: true
secrets:
- name: demo
stringData:
DEMO_TOKEN: "not-a-real-secret-replace-me"
extraEnv: |
- name: DEMO_TOKEN
valueFrom:
secretKeyRef:
name: "{{ .Release.Name }}-demo"
key: DEMO_TOKEN
ingress:
enabled: false
backend:
enabled: false
EOF
Render and inspect before applying:
helm template simple-app \
oci://harbor.ivia.ch/ivia-generic-helm-chart/ivia-generic-helm-chart \
--version 4.8.0 -n tutorial -f work/simple-app-config.yaml \
| grep -E 'kind: ConfigMap|kind: Secret|mountPath:|environment.txt|DEMO_TOKEN'
You should see both a ConfigMap and a Secret object in the rendered output, plus the mount and env references. Chart-managed Secrets are named {{ .Release.Name }}-<name> (here simple-app-demo).
Apply and validate
helm upgrade --install simple-app \
oci://harbor.ivia.ch/ivia-generic-helm-chart/ivia-generic-helm-chart \
--version 4.8.0 -n tutorial --atomic --timeout 5m \
-f work/simple-app-config.yaml
kubectl rollout status deployment/simple-app-frontend -n tutorial --timeout=120s
kubectl port-forward service/simple-app-frontend 8080:8080 -n tutorial
In a second terminal:
curl --fail http://localhost:8080/environment.txt
kubectl get configmap simple-app-frontend-page -n tutorial -o yaml
kubectl get secret simple-app-demo -n tutorial -o jsonpath='{.data.DEMO_TOKEN}' | base64 --decode; echo
kubectl exec deploy/simple-app-frontend -n tutorial -- printenv DEMO_TOKEN
You should see the banner text, a ConfigMap containing that text, and the decoded / in-pod Secret value not-a-real-secret-replace-me.
Change only the ConfigMap text in the temporary values file, repeat helm upgrade, and curl again. A fresh Pod mounts the current ConfigMap. Use extraEnv for scalar process settings, mounted ConfigMaps for files, and Secrets or an external secret manager for credentials.
Expected validation
Curl prints Hello from Minikube configuration!. The image remains simple-static:local; only release configuration and the Pod revision change. The dummy Secret is readable in the Pod as DEMO_TOKEN and is clearly labeled as non-production data.
Troubleshooting and common pitfalls
- Mounted path becomes a directory: include
subPathwhen mounting one key as one file. - ConfigMap or Secret not found: chart names include release and component prefixes; preserve the templated name exactly.
- Old content remains: wait for rollout completion. Kubernetes does not update a
subPathmount in place. - YAML template breaks: preserve single quotes around
configMapNameand Secret names. - Credentials in ConfigMap: ConfigMaps are not secret. Never commit real passwords or tokens.
- “Secrets are encrypted”: base64 is encoding, not encryption. Anyone with read access to the Secret can decode it.
- Editing ConfigMap or Secret directly: the next Helm upgrade restores values; make values the source of truth.
- Chart rejects
secrets/extraEnvkeys: compare field names with a successfulhelm templatefrom this chapter; adjust to the chart’s documented keys if your environment uses a fork.
Recap and practice
Recap: ConfigMaps carry non-sensitive runtime files; Secrets carry credentials that still need access control. One image serves many environments when configuration stays outside the build.
Try to explain in your own words:
- Why rebuild is the wrong response to changing an environment banner?
- Why is base64 on a Secret not enough protection?
Exercise: Change the ConfigMap string to a different greeting, upgrade, and confirm curl shows the new text without rebuilding the image.
Stretch: Move the banner from a mounted file to an extraEnv variable (if you also teach nginx to print env—or simply print the env with kubectl exec) and note which approach is easier for multi-line content.