Env Vars and Secrets
Scenario
You accidentally hardcoded a database password in your code. You pushed it to GitHub. Within 5 minutes, an automated bot scrapes it and drops your production database.
Mental model
The '12-Factor App' rule: Configuration should be strictly separated from code. Code lives in Git; secrets live in the environment.
Deep dive
Environment variables are key-value pairs managed by the operating system. They allow you to change an application's behavior (e.g., pointing to a staging DB vs a prod DB) without changing the code.
In FastAPI and Pydantic v2, `pydantic-settings` is the standard way to read and validate environment variables. It automatically loads variables and converts them to the correct Python types.
Locally, you store secrets in a `.env` file, which is strictly added to `.gitignore`. In production, they are injected by the hosting provider (Docker, AWS, Vercel).
Code examples
Bad: Hardcoded config
DB_URL = 'postgresql://user:pass@localhost:5432/db'
SECRET_KEY = 'super_secret_key'
# Do not do this. It leaks secrets and requires a code change to swap environments.
Hardcoded secrets are a critical security vulnerability.
Good: Pydantic Settings (Ground Truth)
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
# Automatically reads from .env file
model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8')
# Instantiate once, globally or via a dependency
settings = Settings()
If `DATABASE_URL` is missing from the environment, Pydantic will crash the app immediately on startup, which is exactly what you want (Fail Fast).
Common mistakes
- Committing the .env file: If you forget to add `.env` to `.gitignore`, you will push secrets to the repository. If this happens, you must invalidate and rotate the keys immediately; deleting the file from Git history isn't enough.
Recall questions
- What is the 12-Factor App principle regarding configuration?
- Why is `pydantic-settings` preferred over `os.environ.get()`?
- What happens if a required field in a Pydantic Settings class is missing from the environment?
Questions & answers
How do you handle local development secrets versus production secrets?
Locally, I use a `.env` file (which is gitignored). In production, I configure the environment variables directly in the deployment platform (e.g. AWS Secrets Manager, Kubernetes Secrets, or the PaaS dashboard).
What is the problem with using `os.getenv('DEBUG', 'False')` in an `if` statement?
`os.getenv` returns a string. Both `'True'` and `'False'` evaluate to truthy in Python. Pydantic settings properly coerces the string `'False'` to a boolean `False`.