Day 30: The Multi-Environment Magic Show

Usman JapUsman Jap
3 min read

Setting the Stage: What’s Multi-Environment Configs?

Imagine your app as a theater production. In development, you’re rehearsing with a small crew (light resources, debug logs). In staging, you’re doing a dress rehearsal with a bigger setup (more replicas, stricter settings). In production, it’s showtime with a full audience (high availability, tight security). Docker Compose’s multi-environment configs let you switch between these “stages” without rewriting your script (aka docker-compose.yml). Using .env files, override YAMLs, and variable substitution, I made my Flask + Redis app shine in dev, staging, and prod—each with its own vibe!

Act 1: Crafting Environment-Specific .env Files

First, I navigated to ~/tmp/flask-redis-app and whipped up three .env files using vi (because nano is so last week 😉):

  • .env.dev:

      FLASK_ENV=development
      APP_TITLE=Dev Flask App
      REDIS_HOST=redis-service
      LOG_LEVEL=DEBUG
    
  • .env.staging:

      FLASK_ENV=staging
      APP_TITLE=Staging Flask App
      REDIS_HOST=redis-service
      LOG_LEVEL=INFO
    
  • .env.prod:

      FLASK_ENV=production
      APP_TITLE=Prod Flask App
      REDIS_HOST=redis-service
      LOG_LEVEL=ERROR
      REDIS_PASSWORD=my-redis-password
    

These files are like costume changes for my app—each sets the mood (and settings) for its environment. I used vi, hitting i to insert, typing away, and saving with Esc, :wq. Easy peasy!

Act 2: Override Files for the Win

Next, I created override files (docker-compose.dev.yml, docker-compose.staging.yml, docker-compose.prod.yml) to tweak settings like ports and replicas. For example, docker-compose.prod.yml:

services:
  web-prod:
    environment:
      - FLASK_ENV=${FLASK_ENV}
      - APP_TITLE=${APP_TITLE}
      - LOG_LEVEL=${LOG_LEVEL}
      - REDIS_HOST=${REDIS_HOST}
      - REDIS_PASSWORD=${REDIS_PASSWORD}
    deploy:
      replicas: 3
  nginx-prod:
    ports:
      - "8082:80"

These overrides layer on top of my base docker-compose.yml, letting me scale to three replicas in production or map different ports (e.g., 8080 for dev, 8081 for staging). Variable substitution (${VARIABLE}) is the secret sauce, pulling values from the .env files. It’s like giving my app a wardrobe that fits every occasion!

Act 3: The Redis Authentication Drama 😱

Midway through testing, disaster struck! My production containers (web-prod-1) were marked “unhealthy” with a dreaded redis.exceptions.AuthenticationError: Authentication required. Turns out, my Redis service in production used requirepass my-redis-password, but my Flask app wasn’t sending the password. No wonder it was throwing a tantrum!

I dove into app/app.py with vi and updated the Redis connection:

redis = redis.Redis(
    host=os.getenv('REDIS_HOST', 'redis-service'),
    port=6379,
    password=os.getenv('REDIS_PASSWORD'),
    decode_responses=True
)

I added REDIS_PASSWORD=my-redis-password to .env.prod and updated docker-compose.prod.yml to pass it to web-prod. After rebuilding with:

docker compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up -d --build --wait

I ran docker compose ps and saw all containers glowing “Healthy.” A quick curl http://localhost:8082 confirmed: “Prod Flask App: Visited 1 times.” Crisis averted! 🎉 I committed the fix to GitHub with:

git add app/app.py .env.prod docker-compose.prod.yml
git commit -m "Fix Redis authentication for production"
git push origin main

Act 4: Mastering Commands and Grok Queries

To lock in my skills, I practiced multi-environment commands:

  • docker compose --env-file .env.staging config: Validated my staging setup.

  • docker compose exec web env: Peeked at environment variables.

  • docker compose ps: Checked running services.

I also tapped Grok’s brain on grok.com, asking, “How does variable substitution improve Docker Compose flexibility?” Grok explained it makes configs reusable and CI/CD-friendly—super handy! Using DeepSearch, I found an X post from 2025 praising .env files for simplifying deployments. I jotted insights in day_30_notes.txt for active recall, reinforcing my learning.

Learning Hacks: Spaced Repetition and Active Recall

To keep my brain sharp, I used spaced repetition to review Day 27’s named volumes and Day 28’s CPU pinning, skipping overlapping topics like Day 19’s secrets. For active recall, I wrote a 50-word summary of multi-environment benefits in day_30_notes.txt: “Multi-environment configs make deployments flexible, reusable, and scalable. Using .env files and overrides, I tailored my Flask app for dev, staging, and prod without code changes. Variable substitution simplifies setup, and X posts confirm it’s a 2025 must-have!”

Why This Matters

Multi-environment configs are a game-changer for real-world apps. They let you test in dev, refine in staging, and shine in production without rewriting code. Fixing the Redis authentication bug taught me to double-check environment-specific settings— a lesson I’ll carry to Kubernetes next week!

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