Day 8: Securing My Flask App with Docker Compose Env Vars and Secrets!

Usman JapUsman Jap
4 min read

Day 8 was a deep dive into Docker Compose environment variables and secrets, leveling up my Flask + Redis app with secure configurations. I wrestled with errors, explored 2025 DevOps trends on X, and leaned on Grok to sharpen my skills. Buckle up for a 1500-word journey through secure DevOps, with a sprinkle of 2025 flair!

Kicking Off with Environment Variables

After conquering scaling and volumes on Day 7, I shifted gears to make my Flask + Redis app more portable and secure using environment variables and secrets. My goal was to configure the app dynamically, avoiding hardcoded values, and protect sensitive data like passwords. I started in my Ubuntu environment:

bash

cd ~/tmp/flask-redis-app

My docker-compose.yml from Day 7 defined a web (Flask) and redis service, with a redis-data volume. I modified app/app.py to use environment variables for the Redis host and app title:

python

import os
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis_host = os.getenv('REDIS_HOST', 'redis')
app_title = os.getenv('APP_TITLE', 'My Flask App')
redis = Redis(host=redis_host, port=6379)
@app.route('/')
def hello():
    count = redis.incr('hits')
    return f'{app_title}: Visited {count} times.'
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

I updated docker-compose.yml to include environment variables:

yaml

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - REDIS_HOST=redis
      - APP_TITLE=Awesome Flask App
    depends_on:
      - redis
    networks:
      - flask-net

I also created an .env file for portability:

bash

echo "REDIS_HOST=redis" > .env
echo "APP_TITLE=App from .env" >> .env

Running docker compose up -d --build and hitting http://localhost:5000 showed: “App from .env: Visited 1 times.” Success! This made my app environment-agnostic, a 2025 DevOps must-have.

Adding Secrets for Security

Next, I tackled secrets to secure a Redis password. I created redis_password.txt:

bash

echo "supersecretpassword" > redis_password.txt

I updated docker-compose.yml to use secrets:

yaml

services:
  redis:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASSWORD}
    environment:
      - REDIS_PASSWORD=${REDIS_PASSWORD}
    secrets:
      - redis_password
    volumes:
      - redis-data:/data
    networks:
      - flask-net
secrets:
  redis_password:
    file: ./redis_password.txt

I modified app/app.py to read the secret:

python

redis = Redis(host=redis_host, port=6379, password=open('/run/secrets/redis_password').read().strip())

But chaos ensued…

Battling Interpolation Errors

My first attempt to set REDIS_PASSWORD via environment: - REDIS_PASSWORD=$(cat redis_password.txt) triggered:

ERROR: Invalid interpolation format for "environment" option in service "redis": "REDIS_PASSWORD=$(cat redis_password.txt)"

Grok explained: Docker Compose doesn’t support shell substitution ($(cat ...)) in environment. I moved the password to .env:

REDIS_PASSWORD=supersecretpassword

Updated docker-compose.yml to use ${REDIS_PASSWORD}, and it worked! I tested:

bash

docker compose up -d
curl http://localhost:5000

Output: “App from .env: Visited 2 times.” Redis authenticated correctly:

bash

docker exec -it $(docker compose ps -q redis) redis-cli
AUTH supersecretpassword
GET hits

Counter: 2. Persistence held after docker compose down and up -d.

The ContainerConfig Snafu

Another error hit during docker compose up -d --build:

ERROR: for redis 'ContainerConfig'
KeyError: 'ContainerConfig'

This was a compatibility issue with an outdated Compose version (1.29.2). I upgraded to v2.36.2:

bash

sudo rm /usr/bin/docker-compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Verified:

bash

docker compose version

Output: Docker Compose version v2.36.2. With Docker 28.1.1, I reran and the error vanished.

The --resolve-env Misstep

I tried inspecting variables with:

bash

docker compose config --resolve-env

But got:

unknown flag: --resolve-env

Grok clarified: --resolve-env doesn’t exist! docker compose config already resolves variables. Running:

bash

docker compose config

Showed REDIS_PASSWORD: supersecretpassword. Lesson learned: stick to documented flags.

Mastering Configuration Commands

I practiced Day 8 commands:

  • docker compose config: Validated YAML and variable substitution.

  • docker compose run web env: Showed REDIS_HOST=redis, APP_TITLE=App from .env.

  • docker compose run redis cat /run/secrets/redis_password: Confirmed supersecretpassword.

I tested an external secret:

bash

echo "newsecretpass" > external_redis_password.txt

Updated docker-compose.yml:

yaml

secrets:
  redis_password:
    file: ./external_redis_password.txt

Reran and verified with redis-cli AUTH newsecretpass. This mimicked 2025’s dynamic secret management.

Grok’s Wisdom and X Insights

Grok’s think mode shone with queries:

  • “Explain .env files in Docker Compose in 100 words”: They store key-value pairs for portability, loaded automatically, overridden by environment.

  • “Best practices for secrets in 150 words”: Use secrets for sensitive data, mount at /run/secrets/, integrate with Vault in 2025 fintech apps.

  • “Environment variables portability in 100 words”: They decouple configs, enabling seamless environment switches.

Using DeepSearch, I found X posts on 2025 Compose environment variables:

  • @TheDockerDev

    (May 21, 2025): “Advanced patterns for on-prem SaaS, secure secret management.” (140 views, 5 faves). Secrets complement env vars for secure configs.

  • @DOh_Bams

    (May 26, 2025): “Best practices like secrets, health checks.” (627 views, 26 faves). Env vars underpin these practices.

A 50-word summary for @TheDockerDev:

@TheDockerDev

shares advanced Docker Compose patterns for 2025 on-prem SaaS, emphasizing secure secret management. Environment variables configure services dynamically, ensuring portability and security, aligning with scalable DevOps trends.

Error-Fixing Adventures

Day 8 was an error-fixing marathon:

  • Interpolation errors: Fixed by using .env instead of $(cat ...).

  • ContainerConfig: Resolved with Compose v2.36.2.

  • --resolve-env: Dropped for docker compose config.

Grok’s step-by-step guidance was clutch, reinforcing my DevOps debugging skills.

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