Python configuration management via environment variables and typed settings. Use when externalizing config, setting up pydantic-settings, managing secrets, or implementing environment-specific behavior.
Python configuration management via environment variables and typed settings. Use when externalizing config, setting up pydantic-settings, managing secrets, or implementing environment-specific behavior.
Python Configuration Management
Externalize configuration from code using environment variables and typed settings. Well-managed configuration enables the same code to run in any environment without modification.
When to Use This Skill
Setting up a new project's configuration system
Migrating from hardcoded values to environment variables
Implementing pydantic-settings for typed configuration
Required settings should crash the application immediately with a clear error.
from pydantic_settings import BaseSettings
from pydantic import Field, ValidationError
import sys
classSettings(BaseSettings):
# Required - no default means it must be set
api_key: str = Field(alias="API_KEY")
database_url: str = Field(alias="DATABASE_URL")
# Optional with defaults
log_level: str = Field(default="INFO", alias="LOG_LEVEL")
try:
settings = Settings()
except ValidationError as e:
print("=" * 60)
print("CONFIGURATION ERROR")
print("=" * 60)
for error in e.errors():
field = error["loc"][0]
print(f" - {field}: {error['msg']}")
print("\nPlease set the required environment variables.")
sys.exit(1)
A clear error at startup is better than a cryptic None failure mid-request.
Pattern 3: Local Development Defaults
Provide sensible defaults for local development while requiring explicit values for secrets.
classSettings(BaseSettings):
# Has local default, but prod will override
db_host: str = Field(default="localhost", alias="DB_HOST")
db_port: int = Field(default=5432, alias="DB_PORT")
# Always required - no default for secrets
db_password: str = Field(alias="DB_PASSWORD")
api_secret_key: str = Field(alias="API_SECRET_KEY")
# Development convenience
debug: bool = Field(default=False, alias="DEBUG")
model_config = {"env_file": ".env"}
Create a .env file for local development (never commit this):
# .env (add to .gitignore)
DB_PASSWORD=local_dev_password
API_SECRET_KEY=dev-secret-key
DEBUG=true
Pattern 4: Namespaced Environment Variables
Prefix related variables for clarity and easy debugging.
Detailed sections (starting with ## Advanced Patterns) live in references/details.md. Read that file when the navigation summary above is insufficient.
Best Practices Summary
Never hardcode config - All environment-specific values from env vars
Use typed settings - Pydantic-settings with validation
Fail fast - Crash on missing required config at startup
Provide dev defaults - Make local development easy
Never commit secrets - Use .env files (gitignored) or secret managers
Namespace variables - DB_HOST, REDIS_URL for clarity
Import settings singleton - Don't call os.getenv() throughout code
Document all variables - README should list required env vars
Validate early - Check config correctness at boot time
Use secrets_dir - Support mounted secrets in containers