Managing environment variables across different environments can be tricky.Managing environment variables across different environments can be tricky.

Docker Environment Variables: Development vs Production Made Simple

2025/10/17 13:43
5 min read
For feedback or concerns regarding this content, please contact us at crypto.news@mexc.com

When building applications with Docker, managing environment variables across different environments can be tricky. Here’s a clean, practical approach we’ll demonstrate with a real FastAPI application that keeps development smooth while ensuring production deployments are secure.

The Problem We’re Solving

Most developers start with a simple setup that works locally but creates problems in production:

# This works locally but causes issues in production

version: '3.8'  services:    my-app:      build: .      env_file: .env  # This will override production env vars! 

\ Problems:

  • .env file overrides production environment variables
  • Sensitive data accidentally gets deployed
  • Production servers can’t inject their own configuration

Our Solution: Clean Separation with Docker Compose

We’ll build a FastAPI application that demonstrates the right way to handle this.

The Application Structure

fastapi-docker-env-demo/  ├── main.py # FastAPI application ├── requirements.txt # Dependencies ├── Dockerfile # Production-ready container ├── docker-compose.yml # Base config (production-ready) ├── docker-compose.override.yml # Development overrides ├── .env # Local development variables └── .env.production.example # Production reference 

Step 1: FastAPI Application with Environment Config

main.py:

import os from fastapi import FastAPI import uvicorn from dotenv import load_dotenv  # Load environment variables from .env file if it exists load_dotenv()  app = FastAPI(     title="Docker Environment Demo",     description="Demonstrating Docker environment variable best practices",     versi )  # Environment configuration with sensible defaults class Config:     # Application settings     APP_NAME = os.getenv("APP_NAME", "Docker Environment Demo")     ENVIRONMENT = os.getenv("ENVIRONMENT", "development")     DEBUG = os.getenv("DEBUG", "false").lower() == "true"     HOST = os.getenv("HOST", "0.0.0.0")     PORT = int(os.getenv("PORT", "8000"))      # Database settings     DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./dev.db")      # API settings     API_KEY = os.getenv("API_KEY", "dev-api-key-12345")     SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key")      # Third-party services     REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")     EMAIL_SERVICE_URL = os.getenv("EMAIL_SERVICE_URL", "https://api.dev-email-service.com")      # Feature flags     ENABLE_CACHING = os.getenv("ENABLE_CACHING", "true").lower() == "true"     ENABLE_ANALYTICS = os.getenv("ENABLE_ANALYTICS", "false").lower() == "true"  config = Config()  @app.get("/") async def root():     """Welcome endpoint with basic app info"""     return {         "message": f"Welcome to {config.APP_NAME}!",         "environment": config.ENVIRONMENT,         "debug": config.DEBUG,         "version": "1.0.0"     }    @app.get("/env") async def get_environment_variables():     """Shows all environment variables currently loaded by the app"""     return {         "APP_NAME": config.APP_NAME,         "ENVIRONMENT": config.ENVIRONMENT,         "DEBUG": config.DEBUG,         "HOST": config.HOST,         "PORT": config.PORT,         "DATABASE_URL": config.DATABASE_URL,         "API_KEY": config.API_KEY,         "SECRET_KEY": config.SECRET_KEY,         "REDIS_URL": config.REDIS_URL,         "EMAIL_SERVICE_URL": config.EMAIL_SERVICE_URL,         "ENABLE_CACHING": config.ENABLE_CACHING,         "ENABLE_ANALYTICS": config.ENABLE_ANALYTICS     }  if __name__ == "__main__":     uvicorn.run(         "main:app",         host=config.HOST,         port=config.PORT,         reload=config.DEBUG,         log_level="debug" if config.DEBUG else "info"     ) 

Step 2: Production-Ready Docker Configuration

Dockerfile:

FROM python:3.11-slim  WORKDIR /app  # Install dependencies COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt  # Copy application COPY . .  # Security: non-root user RUN adduser --disabled-password appuser && chown -R appuser /app USER appuser  EXPOSE 8000  CMD ["python", "main.py"] 

docker-compose.yml (Production-ready base):

version: '3.8' services:   fastapi-app:     build: .     image: fastapi-env-demo:latest     container_name: fastapi-env-demo     ports:       - "8000:8000"     environment:       - PYTHONPATH=/app     restart: unless-stopped 

Step 3: Development Override Configuration

docker-compose.override.yml (Development additions):

version: '3.8'  services:   fastapi-app:     env_file:       - .env     environment:       # Override for development       - ENVIRONMENT=development       - DEBUG=true     volumes:       # Enable hot reload in development       - .:/app     command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] 

\

Step 4: Environment Variable Files

.env (Local development):

# Development environment variables APP_NAME=Docker Environment Demo ENVIRONMENT=development DEBUG=true HOST=0.0.0.0 PORT=8000  # Database settings DATABASE_URL=sqlite:///./dev.db  # API settings API_KEY=dev-api-key-12345 SECRET_KEY=dev-secret-key-for-local-development  # Third-party services REDIS_URL=redis://localhost:6379 EMAIL_SERVICE_URL=https://api.dev-email-service.com  # Feature flags ENABLE_CACHING=true ENABLE_ANALYTICS=false 

.env.production.example (Production reference):

# Set these on your production server APP_NAME=My Production API ENVIRONMENT=production DEBUG=false DATABASE_URL=postgresql://user:pass@db:5432/prod API_KEY=your-production-api-key SECRET_KEY=your-super-secure-production-secret REDIS_URL=redis://redis:6379 EMAIL_SERVICE_URL=https://api.your-email-service.com ENABLE_CACHING=true ENABLE_ANALYTICS=true 

How It Works

Local Development Workflow

# Simply run this - Docker Compose automatically merges the files  docker-compose up --build 

What happens:

  1. Loads base docker-compose.yml
  2. Automatically merges docker-compose.override.yml
  3. Loads your .env file via env_file directive
  4. Enables hot reload and debug mode
  5. Mounts source code for live editing

Production Deployment

# Deploy using only the base configuration docker-compose -f  docker-compose.yml up -d --build 

then using .env.production.example file in the production environment.For example we are deploying to Digitalocean app platform that is using docker registry. you should connect to the docker registry, tag you images like the following then push.

docker tag <image-name>:<tag> <registry-url>/<reg-name>/<image-name>:<tag> docker push <registry-url>/<reg-name>/<image-name>:<tag>

in Digitalocean app platform, you can bluk load the env variables by going to settings, then in the “App-LevelEnvironment Variables” section choose “Bulk Editor” like here:

and here you go, let test now

Testing Your Setup

The /env endpoint makes it super easy to verify your configuration:

Development Test

curl http://localhost:8000/env 

Response:

Production Test

curl http://your-server:8000/env 

For example you deploy to Digitalocean app platform that is

Response:

Quick Start

  1. Clone the project
  2. Start development: docker-compose up --build
  3. Visit: http://localhost:8000/env to see your configuration

Conclusion

This approach gives you the best of both worlds:

  • Easy local development with automatic .env loading
  • Clean production deployments that respect server environment variables
  • Same Docker image works in all environments
  • Simple testing with the /env endpoint

The key insight: Keep your base docker-compose.yml production-ready, and use override files for development conveniences.

This pattern scales from simple applications to complex microservices and works with any deployment platform. Your development team gets a smooth workflow, and your production deployments stay secure and maintainable.

The complete working example is available as a FastAPI application that demonstrates all these concepts in action.

Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact crypto.news@mexc.com for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

SDNY October retrial date for Tornado Cash’s Storm draws Uniswap comparison

SDNY October retrial date for Tornado Cash’s Storm draws Uniswap comparison

The post SDNY October retrial date for Tornado Cash’s Storm draws Uniswap comparison appeared on BitcoinEthereumNews.com. Commentators in the crypto sector have
Share
BitcoinEthereumNews2026/03/11 00:39
Polymarket, Peter Thiel’s Palantir Eye ‘Surveillance Models’ for Sports Prediction Markets

Polymarket, Peter Thiel’s Palantir Eye ‘Surveillance Models’ for Sports Prediction Markets

The post Polymarket, Peter Thiel’s Palantir Eye ‘Surveillance Models’ for Sports Prediction Markets appeared on BitcoinEthereumNews.com. In brief Polymarket is
Share
BitcoinEthereumNews2026/03/11 00:23
Analyst Predicts ‘Uptober’ Rally for BTC Regardless of FOMC Decision

Analyst Predicts ‘Uptober’ Rally for BTC Regardless of FOMC Decision

The post Analyst Predicts ‘Uptober’ Rally for BTC Regardless of FOMC Decision appeared on BitcoinEthereumNews.com. Bitcoin traded at $116,236 as of 14:04 UTC on Sept. 17, up about 1% in the past 24 hours, holding above a key level as markets await the Federal Reserve’s policy announcement. Analysts’ comments Dean Crypto Trades noted on X that bitcoin is only about 7% above its post-election local peak, while the S&P 500 has risen 9% and gold has surged 36% during the same period. He said bitcoin has compressed more than those assets, making it likely to lead the next larger move, though it could form a “lower high” before extending further. He added that ether could join in once it breaks $5,000 and enters price discovery. Lark Davis pointed to bitcoin’s history around September FOMC meetings, saying every September decision since 2020 — except during the 2022 bear market — has preceded a strong rally. He stressed that the pattern is less about the Fed’s rate choice itself and more about seasonal dynamics, arguing that bitcoin tends to thrive in this period heading into “Uptober.” CoinDesk Research’s technical analysis According to CoinDesk Research’s technical analysis data model, bitcoin rose about 0.9% during the Sept. 16–17 analysis window, climbing from $115,461 to $116,520. BTC reached a session high of $117,317 at 07:00 UTC on Sept. 17 before consolidating. Following that peak, bitcoin tested the $116,400–$116,600 range multiple times, confirming it as a short-term support zone. In the final hour of the session, between 11:39 and 12:38 UTC, BTC attempted a breakout: prices moved narrowly between $116,351 and $116,376 before spiking to $116,551 at 12:34 on higher volume. This confirmed a consolidation-breakout pattern, though the gains were modest. Overall, bitcoin remains firm above $116,000, with support around $116,400 and resistance near $117,300. Latest 24-hour and one-month chart analysis The latest 24-hour CoinDesk Data chart, ending 14:04 UTC on…
Share
BitcoinEthereumNews2025/09/18 12:42