Environment Variables in Docker Containers
Applications need configuration — database passwords, API keys, server ports, feature flags. Hard-coding these into your application is a security risk and makes your image inflexible. Environment variables let you inject configuration into a container at runtime, keeping your image generic and your secrets outside the code.
Why Environment Variables Matter
Imagine the same Docker image running in three places: your laptop (development), a test server (staging), and the live server (production). Each environment needs different database credentials and API keys. You do not want to build a separate image for each. Instead, you build one image and pass the right configuration as environment variables when you start each container.
One image, three environments: docker run -e DB_HOST=localhost -e ENV=dev myapp (development) docker run -e DB_HOST=test-db -e ENV=staging myapp (staging) docker run -e DB_HOST=prod-db.rds -e ENV=prod myapp (production) Same image, different behavior through environment variables
Setting Environment Variables with -e
docker run -d \ --name webapp \ -e DATABASE_URL=postgres://user:pass@db:5432/mydb \ -e SECRET_KEY=mysecretkey123 \ -e DEBUG=false \ -e PORT=5000 \ -p 5000:5000 \ my-python-app
Access these inside Python:
import os
db_url = os.environ.get('DATABASE_URL')
secret = os.environ.get('SECRET_KEY')
debug = os.environ.get('DEBUG', 'false').lower() == 'true'
Using an Environment File (.env)
Passing many variables with -e flags gets messy. Use a file instead:
Create a file called .env: DATABASE_URL=postgres://user:pass@db:5432/mydb SECRET_KEY=mysecretkey123 DEBUG=false PORT=5000 REDIS_URL=redis://redis:6379
docker run -d \ --name webapp \ --env-file .env \ -p 5000:5000 \ my-python-app
Add .env to your .gitignore file. Never commit secrets to version control.
Setting ENV in Dockerfile — Build-Time Defaults
You can set default environment variables inside your Dockerfile. These become the defaults when no value is passed at runtime.
FROM python:3.11-slim WORKDIR /app COPY . . RUN pip install -r requirements.txt ENV PORT=5000 ENV DEBUG=false ENV LOG_LEVEL=info CMD ["python", "app.py"]
At runtime, you can override any of these defaults:
docker run -e PORT=8080 -e DEBUG=true my-python-app
Use ENV in Dockerfile for non-secret configuration defaults. Never put passwords or API keys in a Dockerfile — they get baked into the image and become visible to anyone who pulls it.
ARG vs ENV — Build Time vs Run Time
ARG (build-time only): Available during docker build NOT available in the running container Used for build customization (like version numbers) FROM python:3.11-slim ARG APP_VERSION=1.0 RUN echo "Building version $APP_VERSION" # APP_VERSION is gone after build completes Build with: docker build --build-arg APP_VERSION=2.5 . ENV (run-time): Available in the running container Can have defaults set at build time Can be overridden at docker run time
Security — Handling Secrets Properly
❌ Bad: Baking secrets into Dockerfile FROM python:3.11-slim ENV DB_PASSWORD=supersecret ← visible in image history! ❌ Bad: Committing .env to git git add .env && git commit ← credentials exposed forever ✅ Good: Pass at runtime, never build into image docker run -e DB_PASSWORD=$DB_PASSWORD myapp ✅ Good: Use Docker secrets (in Swarm/Kubernetes) or external secret managers (AWS Secrets Manager, Vault)
Inspecting Environment Variables
See all env vars in a running container: docker exec webapp env Or inspect: docker inspect webapp | grep -A 20 '"Env"'
