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"'

Leave a Comment