# Day 34: Taming Kubernetes ConfigMaps and PersistentVolumes Like a Pro!

Usman JapUsman Jap
4 min read

The Mission: ConfigMaps as Volumes and Persistent Storage

Today’s goal was to level up my Flask + Redis app by:

  1. Mounting a ConfigMap as a volume to store Flask’s configuration (like FLASK_ENV and REDIS_HOST) in a file, instead of environment variables.

  2. Adding PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs) to make sure Redis data doesn’t vanish when a pod restarts.

  3. Fixing a pesky bug that had my Flask pod in a CrashLoopBackOff tantrum (more on that later!).

Kicking Off with a Quick Review

I started by firing up Minikube with minikube start --driver=docker and checked my Day 33 setup:

kubectl get pods,svc,configmap,secret
minikube service flask-service --url

My Redis pod (redis-5d7f4c7db8-nfxw7) was happily running, but oh no—my Flask pod (flask-deployment-5b97f5b4f4-6psfd) was stuck in CrashLoopBackOff! A quick kubectl logs revealed the culprit:

NameError: name 'logger' is not defined

Turns out, in my app.py, I was trying to use logger.info before defining the logger. Rookie mistake! I’ll fix that soon, but first, I opened vi ~/flask-redis-app/notes/Day_34_Activities.txt and jotted down the header: Day 34: ConfigMap Volume Mounts and PersistentVolumes - July 4, 2025.

To warm up, I asked myself: How do ConfigMaps as environment variables (Day 33) differ from Docker environment variables (Day 6)? Easy—Kubernetes ConfigMaps are managed centrally and can be updated dynamically, while Docker env vars are static and tied to the container. Then, I asked Grok (in think mode): “What are the benefits of mounting ConfigMaps as volumes versus using environment variables in Kubernetes?” Grok explained that volumes allow file-based configs, support dynamic updates without pod restarts (if the app reloads files), and are great for complex configs. Environment variables, though simpler, need pod restarts for updates. Mind blown!


Mounting ConfigMaps Like a File Wizard

Time to make my Flask app read its config from a file instead of environment variables. I created a ConfigMap (flask-configmap-volume.yaml) with a config.ini file:

vi ~/flask-redis-app/k8s/flask-configmap-volume.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: flask-app-config-volume
data:
  config.ini: |
    [app]
    FLASK_ENV=production
    REDIS_HOST=redis-service
    APP_PORT=5000

Applied it with kubectl apply -f and updated my Flask deployment (flask-deployment.yaml) to mount this striking out environment variables and adding:

spec:
  containers:
  - name: flask
    image: uyap/flask-redis-app_web:latest
    volumeMounts:
    - name: config-volume
      mountPath: /app/config
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: flask-app-config-volume

Now, the fun part—fixing that NameError in app.py. The issue was that I called logger.info before defining logger. I rearranged the code to initialize logging first:

import logging
import configparser

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

config = configparser.ConfigParser()
config.read('/app/config/config.ini')
FLASK_ENV = config['app']['FLASK_ENV']
REDIS_HOST = config['app']['REDIS_HOST']
APP_PORT = int(config['app']['APP_PORT'])
logger.info(f"Loaded config: FLASK_ENV={FLASK_ENV}, REDIS_HOST={REDIS_HOST}, APP_PORT={APP_PORT}")

I rebuilt and pushed the Docker image:

cd ~/tmp/flask-redis-app
docker build -t uyap/flask-redis-app_web:latest .
docker push uyap/flask-redis-app_web:latest
kubectl apply -f ~/flask-redis-app/k8s/flask-deployment.yaml

Tested with kubectl exec -it <flask-pod-name> -- cat /app/config/config.ini—boom, the config file was there! A quick curl <flask-service-url> confirmed the app was running smoothly. I logged in Day_34_Activities.txt: “Mounted ConfigMap as volume—feels like giving Flask a config file it can actually read!”

I asked Grok: “How to update ConfigMap volume mounts without restarting pods?” DeepSearch revealed that Kubernetes updates mounted ConfigMap files automatically (within seconds, thanks to kubelet sync). But, my Flask app needs to poll or watch the file for changes to use them without a restart. Noted for future tweaks!

Making Redis Data Stick with PVs and PVCs

Next, I tackled persistent storage for Redis to ensure data survives pod restarts. I created a PersistentVolume (redis-pv.yaml):

apiVersion: v1
kind: PersistentVolume
metadata:
  name: redis-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /mnt/redis-data

And a PersistentVolumeClaim (redis-pvc.yaml):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Applied them with kubectl apply -f, then updated redis-deployment.yaml:

spec:
  containers:
  - name: redis
    image: redis:latest
    volumeMounts:
    - name: redis-storage
      mountPath: /data
  volumes:
  - name: redis-storage
    persistentVolumeClaim:
      claimName: redis-pvc

After reapplying with kubectl apply -f, I tested persistence:

curl <flask-service-url>/set_key?key=test&value=123
kubectl delete pod -l app=redis
kubectl get pods  # Wait for new pod
curl <flask-service-url>/get_key?key=test

The key test=123 was still there—success! I checked the PV/PVC binding with kubectl describe pvc redis-pvc and logged: “Redis data is now sticky like glue!”

I asked Grok: “What’s the difference between hostPath and other storage classes in Minikube?” DeepSearch explained that hostPath is simple, using Minikube’s local filesystem (e.g., /mnt/redis-data), but data may vanish on restarts if the directory isn’t persistent. Other storage classes, like CSI drivers, offer features like snapshots but need addons. For Minikube, hostPath is perfect for testing.


Polishing the App and Blogging

I added a log to app.py for Redis persistence:

logger.info(f"Redis data persisted at: /data")

Rebuilt, pushed, and redeployed the image, then checked logs with kubectl logs -l app=flask. All good!

Committed to GitHub:

git add .
git commit -m "Day 34: ConfigMap volume mounts and PV/PVC for Redis"
git push origin main

Key Takeaways

Today was a blast! Fixing that NameError felt like solving a puzzle, and seeing Redis data persist was pure joy. ConfigMap volume mounts are like giving your app a config file it can read anytime, and PV/PVCs are the superheroes of data durability. Next up, I’m eyeing StorageClasses or Ingress for Day 35. Stay tuned, and let’s keep rocking this Kubernetes adventure!

0
Subscribe to my newsletter

Read articles from Usman Jap directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Usman Jap
Usman Jap