Env Vars and Secrets

RoadmapsPython Backend

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

Recall questions

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`.

Return to Python Backend Roadmap