Topic 23 of 64
Environment Variables
Overview
Environment variables are the standard way to configure applications without hardcoding secrets. The 12-Factor App methodology mandates this — it keeps credentials out of source code and makes apps portable across environments.
Syntax
python
import os
from dotenv import load_dotenv
# Load from .env file
load_dotenv()
# Read with fallback
DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///dev.db")
DEBUG = os.environ.get("DEBUG", "False").lower() == "true"
PORT = int(os.environ.get("PORT", 8000))
SECRET_KEY = os.environ["SECRET_KEY"] # raises KeyError if missing
# Pydantic Settings (type-safe, recommended)
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
port: int = 8000
allowed_origins: list[str] = ["http://localhost:3000"]
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
settings = Settings()
# settings.database_url — typed, validated, IDE-aware
# Access in app
print(settings.database_url)
print(settings.debug)Common Pitfalls
- Never commit .env files to git — add them to .gitignore. Commit .env.example with placeholder values instead.
- os.environ.get() returns a string — always convert to the correct type (int(), bool(), etc.).
- Interview tip: Pydantic Settings validates and converts environment variables automatically — preferred over raw os.environ in production.
Real-World Example
Multi-environment configuration for a FastAPI production app:
example
python
from pydantic_settings import BaseSettings
from pydantic import PostgresDsn, HttpUrl, validator
from typing import Optional
import os
class Settings(BaseSettings):
# Database
postgres_host: str = "localhost"
postgres_port: int = 5432
postgres_db: str = "devnotes"
postgres_user: str = "postgres"
postgres_password: str
@property
def database_url(self) -> str:
return f"postgresql+asyncpg://{self.postgres_user}:{self.postgres_password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
# Security
secret_key: str
jwt_algorithm: str = "HS256"
access_token_expire_minutes: int = 30
# Email
smtp_host: str = "smtp.gmail.com"
smtp_port: int = 587
smtp_user: Optional[str] = None
smtp_password: Optional[str] = None
# Feature flags
enable_signup: bool = True
max_upload_size_mb: int = 10
class Config:
env_file = f".env.{os.getenv('APP_ENV', 'development')}"
case_sensitive = False
settings = Settings()