Day 27: Mastering Docker Compose Advanced Volume Management with a Flask + Redis App

Usman JapUsman Jap
4 min read

The Mission: Supercharge Our Flask App’s Storage

Today’s goal? Enhance our Flask + Redis app with advanced volume management in Docker Compose. We’re talking named volumes to keep Redis data safe across container restarts, bind mounts with ninja-level access control for Flask configs, and a sprinkle of retention policies to keep things tidy. Plus, I’ll practice some cool volume commands, query Grok for insights.

Oh, and there was a hiccup: a sneaky module 'redis' has no attribute 'incr' error that made curl http://localhost:8080 scream “Error!” Don’t worry—we squashed it and came out stronger. Let’s break it down!


Named Volumes with Retention Policies

First up, I navigated to my Flask + Redis app directory:

cd ~/tmp/flask-redis-app

I created a named volume for Redis data:

docker volume create redis-data

Then, I updated docker-compose.yml to use this volume with a retention policy:

services:
  redis:
    image: redis:latest
    volumes:
      - redis-data:/data
    # ... existing healthcheck, network configs ...
volumes:
  redis-data:
    name: redis-data
    labels:
      - "com.example.retention=7d"
      - "com.example.description=Redis persistent data"

This setup ensures Redis data (like our visits counter) persists across container restarts. The retention=7d label is a cue for cleanup scripts to remove the volume after 7 days.

To automate cleanup, I crafted a script (cleanup_volumes.sh):

#!/bin/bash
docker volume ls --format "{{.Name}} {{.Labels}}" | grep "com.example.retention=7d" | while read -r volume labels; do
  created=$(docker volume inspect "$volume" --format '{{.CreatedAt}}' | date -d - +%s)
  threshold=$(date -d '7 days ago' +%s)
  if [ "$created" -lt "$threshold" ]; then
    docker volume rm "$volume"
  fi
done

I made it executable (chmod +x cleanup_volumes.sh) and tested it—no volumes were removed since redis-data was fresh. To verify persistence, I ran:

docker compose --profile dev up -d --build --wait
docker compose exec redis redis-cli -a "$(cat redis_password.txt)" INCR visits
docker compose down
docker compose --profile dev up -d --wait
docker compose exec redis redis-cli -a "$(cat redis_password.txt)" GET visits

Boom! The visits counter held steady at “1”. Named volumes for the win!

Secure Bind Mounts

Next, I set up a bind mount for Flask’s configuration:

mkdir -p config
echo "FLASK_CONFIG=production" > config/flask.conf
chmod 400 config/flask.conf
sudo chown 1000:1000 config/flask.conf

I updated docker-compose.yml to mount this file read-only:

services:
  web:
    # ... existing config ...
    volumes:
      - ./config/flask.conf:/app/config/flask.conf:ro
  web-prod:
    # ... same as above ...

Then, I modified app/app.py to load the config:

try:
    with open('/app/config/flask.conf', 'r') as f:
        for line in f:
            key, value = line.strip().split('=')
            os.environ[key] = value
    logger.info(f"Loaded config: {os.getenv('FLASK_CONFIG')}")
except FileNotFoundError:
    logger.warning("No config file found, using defaults")

Fixing the Redis Error

Here’s where things got spicy. When I tested the app with curl http://localhost:8080, I got “Error,” and the logs screamed: module 'redis' has no attribute 'incr'. The culprit? My app.py was calling redis_client.incr('visits') instead of redis.incr('visits'). I’d aliased the redis module as redis_client, but the actual client instance was named redis.

I fixed it in app/app.py:

redis = redis_client.Redis(host=redis_host, port=6379, password=redis_password, decode_responses=True)
# ...
@app.route('/')
def index():
    try:
        count = redis.incr('visits')  # Fixed!
        logger.info("Incremented visit count")
        return f"App from .env: Visited {count} times."
    except Exception as e:
        logger.error(f"Error in index: {e}")
        return "Error", 500

After committing the fix (git add, git commit, git push), I restarted the app:

docker compose --profile dev up -d --build --wait
curl http://localhost:8080

Success! The response was: “App from .env: Visited 1 times.” Logs confirmed: Incremented visit count. I also verified the bind mount:

docker compose exec web cat /app/config/flask.conf

Output: FLASK_CONFIG=production. Sweet!


Mastering Volume Commands

I practiced some volume commands to cement my skills:

  • List volumes: docker volume ls (saw redis-data).

  • Inspect volume: docker volume inspect redis-data (confirmed retention=7d label).

  • Check bind mount permissions: docker compose exec web ls -l /app/config/flask.conf (read-only, -r--------).

I also tested a vulnerability by changing the bind mount to writable (:rw), attempting to write to flask.conf:

docker compose exec web bash -c "echo test > /app/config/flask.conf"

It worked (uh-oh!), proving why :ro is critical. I reverted to read-only and committed the change.

Grok Queries for Insights

Grok was my trusty sidekick! I asked:

  • “Explain Docker Compose volume commands in 100 words.” (Got a clear breakdown of docker volume create, ls, and inspect.)

  • “Explain best practices for Docker Compose advanced volume management in 150 words, with a 2025 example.” (Learned about retention labels and secure bind mounts.)

  • “How do named volumes with retention policies improve Docker apps in 100 words?” (Discovered they boost scalability and simplify cleanup.)

  • In think mode: “Step-by-step, how does Docker Compose manage advanced volumes?” (Grok outlined defining, creating, and lifecycle management.)

Tomorrow (Day 28, June 27, 2025), I’ll tackle resource optimization (CPU pinning, memory limits). Stay tuned, and let’s keep rocking this Docker journey!Using DeepSearch, I found a 2025 X post about volume trends, summarizing it in day_27_notes.txt: “Named volumes with labels streamline data management for AI-driven apps, ensuring compliance and scalability.”

Takeaways and Next Steps

Today was a blast! Fixing the Redis error felt like cracking a puzzle, and advanced volume management made my app more robust. Named volumes ensure Redis data sticks around, bind mounts keep configs secure, and retention policies prevent storage chaos.

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