Chapter 6

Externalize Configuration

Move environment-specific content out of the image with a chart-managed ConfigMap and mounted file.

Just started Saved in this browser.

An image should mean the same thing in development and production. Configuration answers a different question: how should that image behave in this environment?

Step by step

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.

Do this

The challenge

Challenge: change the static page's environment banner through a ConfigMap without rebuilding simple-static:local.

Prepare

Prerequisites

  • A running Minikube cluster with the extracted static source.
  • Helm and kubectl.
  • Permission to create a work directory in the extracted package.
Outcome

Learning goals

  • Distinguish build-time assets from runtime configuration
  • Create a ConfigMap through chart values
  • Mount one ConfigMap key as one nginx file
  • Explain why secrets do not belong in ConfigMaps
Step by step

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 -
Step by step

Create configuration values

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
    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|mountPath:|environment.txt'
Step by step

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

Change only the 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.

Verify

Expected validation

Curl prints Hello from Minikube configuration!. The image remains simple-static:local; only release configuration and the Pod revision change.

Troubleshoot

Troubleshooting and common pitfalls

  • Mounted path becomes a directory: include subPath when mounting one key as one file.
  • ConfigMap 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 subPath mount in place.
  • YAML template breaks: preserve single quotes around configMapName.
  • Credentials in ConfigMap: ConfigMaps are not secret. Never commit real passwords or tokens.
  • Editing ConfigMap directly: the next Helm upgrade restores values; make values the source of truth.

Separating configuration from images makes promotion between environments predictable. You are building a strong operational habit.

Reference

Official references

Just started Saved in this browser.