Skip to content

Configuring Django for Local and Production Environments with Dynamic .env Files

When working with Django, it's crucial to manage environment-specific settings securely and efficiently. Using .env files allows you to store configuration variables separately from your codebase, making it easier to handle different environments like local development and production.


Different Approach

Using the DEBUG boolean value to switch between settings.local and settings.prod is a common and practical approach for managing different environments in Django. However, there are other strategies you can consider, each with its own benefits. Here’s a comparison of various approaches and some additional suggestions:

Common Approaches

  1. Using DEBUG Boolean Value:

    Pros:
    • Simple and straightforward.
    • Directly tied to Django's built-in DEBUG setting.
    Cons:
    • Primarily useful for development vs. production environments.
    • Less flexibility for other deployment scenarios (e.g., staging, testing).
  2. Environment-Specific Environment Variables:

    • Setup: Use a dedicated environment variable to specify which settings module to use, such as DJANGO_ENVIRONMENT.

      wsgi.py
      import os
      import environ
      from django.core.wsgi import get_wsgi_application
      
      env = environ.Env()
      environ.Env.read_env()
      
      environment = env('DJANGO_ENVIRONMENT', default='development')
      settings_module = 'dating_app.settings.local' if environment == 'development' else 'dating_app.settings.prod'
      
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)
      application = get_wsgi_application()
      
    Pros
    • More flexible, allowing for additional environments (e.g., staging, testing).
    • Avoids coupling environment-specific settings with DEBUG.
    Cons
    • Requires setting up and managing additional environment variables.
  3. Using Different .env Files:

    • Setup: Maintain different .env files for different environments and load the appropriate one based on a primary environment variable.

      wsgi.py
      import os
      import environ
      from django.core.wsgi import get_wsgi_application
      
      env = environ.Env()
      environment = env('DJANGO_ENVIRONMENT', default='development')
      
      if environment == 'production':
          env_file = '.env.prod'
      else:
          env_file = '.env.local'
      
      environ.Env.read_env(env_file)
      
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', f'dating_app.settings.{environment}')
      application = get_wsgi_application()
      
    Pros
    • Provides a clear separation between different configurations.
    • Easy to manage and switch between environments.
    Cons
    • Requires careful management of multiple .env files.
  4. Deployment-Specific Configuration:

    • Setup: Configure the settings module directly within your deployment environment (e.g., using Docker, Kubernetes, or a cloud service's environment variable configuration).
    • Example: Set DJANGO_SETTINGS_MODULE in your Dockerfile or deployment configuration.
    Pros
    • Seamless integration with deployment tools and platforms.
    • Maintains clear separation of configuration and code.
    Cons
    • Less control in local development environments if not configured properly.

Recommendations

  1. For Development and Production: Using the DEBUG boolean value is suitable and easy for projects with only development and production environments. It aligns well with Django’s built-in DEBUG setting and is easy to implement.

  2. For More Complex Scenarios: If you have multiple environments (e.g., staging, testing), consider using an environment-specific environment variable (DJANGO_ENVIRONMENT) or separate .env files. This approach provides more flexibility and avoids tying environment configurations directly to DEBUG.

  3. Deployment Tools Integration: For modern deployment practices using containers or cloud platforms, configure DJANGO_SETTINGS_MODULE directly within the deployment environment. This method aligns well with containerized deployments and cloud services.

Conclusion

Each approach has its strengths, and the best choice depends on your specific needs and deployment scenarios. For many projects, starting with the DEBUG boolean value is sufficient and straightforward. As your project grows and your deployment needs become more complex, consider adopting more flexible strategies for managing environment-specific configurations.


Method 1

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
import environ

def main():
    """Run administrative tasks."""
    env = environ.Env()
    # Read environment variables from the .env file
    environ.Env.read_env()  

    # Get the DEBUG flag from the environment, default to False if not set
    DEBUG = env.bool('DEBUG', default=False)

    # Set the appropriate settings module based on DEBUG
    if DEBUG:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dating_app.settings.local')
    else:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dating_app.settings.prod')

    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

if __name__ == '__main__':
    main()
import os
import environ
from django.core.wsgi import get_wsgi_application

# Initialize environment variables
env = environ.Env()
environ.Env.read_env()  # Reads environment variables from .env file

# Determine the settings module based on DEBUG
DEBUG = env.bool('DEBUG', default=False)
settings_module = 'dating_app.settings.local' if DEBUG else 'dating_app.settings.prod'

# Set the Django settings module environment variable
os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

application = get_wsgi_application()
import os
import environ
from django.core.asgi import get_asgi_application

# Initialize environment variables
env = environ.Env()
environ.Env.read_env()  # Reads environment variables from .env file

# Determine the settings module based on DEBUG
DEBUG = env.bool('DEBUG', default=False)
settings_module = 'dating_app.settings.local' if DEBUG else 'dating_app.settings.prod'

# Set the Django settings module environment variable
os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

application = get_asgi_application()

Method 2

os.getenv()

The os.getenv('DJANGO_ENVIRONMENT', 'local') line in your code is designed to get the value of the DJANGO_ENVIRONMENT environment variable. If DJANGO_ENVIRONMENT is not set, it defaults to 'local'. This allows your application to decide which settings to use based on the environment it's running in. Here's how it works in different scenarios:

How It Works
  1. Local Development:

    • When you run your application locally, you typically set the DJANGO_ENVIRONMENT variable in your local .env file.
    • For instance, your local .env file might contain:

      DJANGO_ENVIRONMENT=local
      
    • This ensures that the application reads the correct environment-specific .env file (e.g., .env.local).

  2. Deployment:

    • On your deployment platform (like Heroku, Docker, AWS, etc.), you set the DJANGO_ENVIRONMENT environment variable to the appropriate value for the environment.
    • For example, in a production environment, you would set DJANGO_ENVIRONMENT to prod:

      export DJANGO_ENVIRONMENT=prod
      
    • The deployment environment should be configured to use the prod setting and read the .env.prod file.

Example Code Breakdown

Here's a more detailed example to clarify:

manage.py
import os
import sys
import environ

def main():
    """Run administrative tasks."""
    env = environ.Env()

    # Determine the environment and load the corresponding .env file
    environment = os.getenv('DJANGO_ENVIRONMENT', 'local')
    env_file = f'.env.{environment}'
    env.read_env(env_file)

    settings_module = f'your_project.settings.{environment}'
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

if __name__ == '__main__':
    main()
Explanation:
  • os.getenv('DJANGO_ENVIRONMENT', 'local') retrieves the value of DJANGO_ENVIRONMENT from the environment.
    • If DJANGO_ENVIRONMENT is set (e.g., to prod in production), it will use that value.
    • If DJANGO_ENVIRONMENT is not set, it defaults to 'local'.
  • Based on the value of DJANGO_ENVIRONMENT, the appropriate .env file is loaded (.env.prod, .env.local, etc.).
  • The settings module is then set according to the environment (e.g., your_project.settings.prod).
Deployment Configuration
  1. On Local Development:

    • Ensure your .env file contains DJANGO_ENVIRONMENT=local.
  2. On Production:

    • Set the DJANGO_ENVIRONMENT variable in your deployment configuration or environment settings.
    • For example, in Heroku, you can set environment variables through the Heroku CLI:

      heroku config:set DJANGO_ENVIRONMENT=prod
      
    • In Docker, you might configure it in your Dockerfile or docker-compose.yml:

      ENV DJANGO_ENVIRONMENT=prod
      

Summary

The os.getenv('DJANGO_ENVIRONMENT', 'local') approach allows you to dynamically load configuration based on the environment. The default value 'local' ensures that if DJANGO_ENVIRONMENT is not set (e.g., during local development), it will use the local settings. When deploying, you should configure the environment variable to reflect the correct environment (e.g., prod), so your application reads the appropriate settings and .env file.


Method 3

Certainly! Here’s a more detailed example of how to manage multiple environments (e.g., development, staging, production) using an environment-specific environment variable (DJANGO_ENVIRONMENT) and separate .env files for each environment.

Directory Structure

Assume the following directory structure for your Django project:

your_project/
    settings/
        __init__.py
        base.py
        local.py
        staging.py
        prod.py
    .env.local
    .env.staging
    .env.prod
    manage.py
    wsgi.py
    asgi.py

1. Define Your Settings Files

  • settings/base.py: Common settings for all environments

    # settings/base.py
    
    import os
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    SECRET_KEY = os.getenv('SECRET_KEY', 'default-secret-key')
    INSTALLED_APPS = [
        # common apps
    ]
    MIDDLEWARE = [
        # common middleware
    ]
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    # Other common settings
    
  • settings/local.py: Development-specific settings

    # settings/local.py
    
    from .base import *
    
    DEBUG = True
    ALLOWED_HOSTS = []
    
    DATABASES['default'] = {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('DEV_DB_NAME'),
        'USER': os.getenv('DEV_DB_USER'),
        'PASSWORD': os.getenv('DEV_DB_PASSWORD'),
        'HOST': os.getenv('DEV_DB_HOST'),
        'PORT': os.getenv('DEV_DB_PORT'),
    }
    
  • settings/staging.py: Staging-specific settings

    # settings/staging.py
    
    from .base import *
    
    DEBUG = False
    ALLOWED_HOSTS = ['staging.example.com']
    
    DATABASES['default'] = {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('STAGING_DB_NAME'),
        'USER': os.getenv('STAGING_DB_USER'),
        'PASSWORD': os.getenv('STAGING_DB_PASSWORD'),
        'HOST': os.getenv('STAGING_DB_HOST'),
        'PORT': os.getenv('STAGING_DB_PORT'),
    }
    
  • settings/prod.py: Production-specific settings

    # settings/prod.py
    
    from .base import *
    
    DEBUG = False
    ALLOWED_HOSTS = ['example.com']
    
    DATABASES['default'] = {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('PROD_DB_NAME'),
        'USER': os.getenv('PROD_DB_USER'),
        'PASSWORD': os.getenv('PROD_DB_PASSWORD'),
        'HOST': os.getenv('PROD_DB_HOST'),
        'PORT': os.getenv('PROD_DB_PORT'),
    }
    

2. Create Environment-Specific .env Files

  • .env.local: Development environment variables

    DJANGO_ENVIRONMENT=local
    SECRET_KEY=your-local-secret-key
    DEV_DB_NAME=dev_db
    DEV_DB_USER=dev_user
    DEV_DB_PASSWORD=dev_password
    DEV_DB_HOST=localhost
    DEV_DB_PORT=5432
    
  • .env.staging: Staging environment variables

    DJANGO_ENVIRONMENT=staging
    SECRET_KEY=your-staging-secret-key
    STAGING_DB_NAME=staging_db
    STAGING_DB_USER=staging_user
    STAGING_DB_PASSWORD=staging_password
    STAGING_DB_HOST=localhost
    STAGING_DB_PORT=5432
    
  • .env.prod: Production environment variables

    DJANGO_ENVIRONMENT=prod
    SECRET_KEY=your-prod-secret-key
    PROD_DB_NAME=prod_db
    PROD_DB_USER=prod_user
    PROD_DB_PASSWORD=prod_password
    PROD_DB_HOST=localhost
    PROD_DB_PORT=5432
    

Update manage.py, wsgi.py, asgi.py

Modify manage.py, wsgi.py and asgi.py to use the DJANGO_ENVIRONMENT variable and load the appropriate .env file:

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
import environ

def main():
    """Run administrative tasks."""
    env = environ.Env()

    # Determine the environment and load the corresponding .env file
    environment = os.getenv('DJANGO_ENVIRONMENT', 'local')
    env_file = f'.env.{environment}'

    env.read_env(env_file)

    settings_module = f'dating_app.settings.{environment}'
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

if __name__ == '__main__':
    main()
import os
import environ
from django.core.wsgi import get_wsgi_application

env = environ.Env()
environment = os.getenv('DJANGO_ENVIRONMENT', 'local')
env_file = f'.env.{environment}'
env.read_env(env_file)

settings_module = f'dating_app.settings.{environment}'
os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

application = get_wsgi_application()
import os
import environ
from django.core.asgi import get_asgi_application

env = environ.Env()
environment = os.getenv('DJANGO_ENVIRONMENT', 'local')
env_file = f'.env.{environment}'
env.read_env(env_file)

settings_module = f'dating_app.settings.{environment}'
os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

application = get_asgi_application()

Summary

By using environment-specific environment variables and .env files, you gain the flexibility to manage different environments (development, staging, production) more efficiently. Each environment can have its own configuration without relying solely on the DEBUG flag, which improves clarity and maintainability.


Env Example

# .env

DEBUG=True
SECRET_KEY='your-secret-key'
DEV_DB_NAME='dev_db'
DEV_DB_USER='dev_user'
DEV_DB_PASSWORD='dev_password'
DEV_DB_HOST='localhost'
DEV_DB_PORT='5432'
PROD_DB_NAME='prod_db'
PROD_DB_USER='prod_user'
PROD_DB_PASSWORD='prod_password'
PROD_DB_HOST='localhost'
PROD_DB_PORT='5432'

Set Environment Variable in Ubuntu

On your Ubuntu system, you need to set the DJANGO_ENVIRONMENT environment variable to indicate which environment you are in. You can set this variable temporarily in the terminal or permanently by adding it to your profile.

To list or view the value of the DJANGO_ENVIRONMENT

  • To list or view the value of the DJANGO_ENVIRONMENT environment variable on your system,

    echo $DJANGO_ENVIRONMENT
    
  • List All Environment Variables:

    printenv
    
    # or
    
    env
    

Temporarily Setting Environment Variable

  • To set an environment variable for the duration of the terminal session:

    export VARIABLE_NAME=value
    
  • In your terminal, you can set the environment variable before running your Django commands:

    export DJANGO_ENVIRONMENT=prod  # or local, staging
    python manage.py runserver
    
  • You can then verify it using:

    echo $DJANGO_ENVIRONMENT
    

Permanently Setting Environment Variable

  • To make the environment variable permanent for your user, add it to your .bashrc or .bash_profile file:

    1. Open your .bashrc file in a text editor:

      nano ~/.bashrc
      
    2. Add the environment variable at the end of the file:

      export DJANGO_ENVIRONMENT=prod  # or local, staging
      
    3. Save the file and reload the shell configuration:

      source ~/.bashrc
      
  • Alternatively, you can use .profile or .zshrc if you use a different shell.

Summary

By using environment-specific .env files and setting the DJANGO_ENVIRONMENT variable, you can dynamically configure Django to use different settings based on the environment. In Ubuntu, you set the environment variable in your shell or profile to ensure Django loads the correct .env file. This approach helps manage different configurations for development, staging, and production environments efficiently.