FastAPI Environment Settings

Every application carries values that change between computers: database addresses, secret keys, debug switches. Typing those values straight into the code creates two problems. Secrets leak when the code reaches a public repository, and every new environment demands a code edit. Environment settings move these values outside the code. Picture a hotel key card: the door stays the same, while each guest receives a different card.

Installing pydantic-settings

FastAPI developers read settings with a small companion package.

pip install pydantic-settings

Creating a Settings Class

A settings class looks like a Pydantic model. Each field names one setting.

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    app_name: str = "My API"
    debug: bool = False
    database_url: str
    secret_key: str

    model_config = SettingsConfigDict(env_file=".env")

settings = Settings()

Fields with a default, such as app_name, stay optional. Fields without a default, such as database_url, must exist or the app refuses to start. That early failure beats a mysterious crash later.

The .env File

Create a file named .env in the project root. Each line holds one setting.

DATABASE_URL=sqlite:///./app.db
SECRET_KEY=change-this-to-a-long-random-value
DEBUG=true

Names match fields without regard to capital letters. DATABASE_URL fills database_url.

Where Values Come From

Priority (highest wins)

  1. Real environment variables       export DEBUG=false
  2. Values in the .env file          DEBUG=true
  3. Defaults in the class            debug: bool = False

  Result for "debug":  false   (the real environment variable wins)

Servers in production usually set real environment variables. Developers keep a .env file on their own laptops. The same code works in both places.

Keeping Secrets Safe

Add the file to .gitignore so it never reaches version control.

.env

Share a template named .env.example instead. It lists every setting with fake values, so teammates know what to fill in.

Using Settings in Routes

Wrap the settings object in a function and cache it. Dependency injection hands the same object to every route.

from functools import lru_cache
from fastapi import Depends, FastAPI

@lru_cache
def get_settings():
    return Settings()

app = FastAPI()

@app.get("/info")
def info(settings: Settings = Depends(get_settings)):
    return {"app": settings.app_name, "debug": settings.debug}

The lru_cache decorator reads the file once. Tests can swap in different settings through dependency overrides.

How the Flow Looks

  .env file / server variables
              │
              ▼
     Settings class  ──▶  validates types  ──▶  settings object
              │
              ▼
   Depends(get_settings)  ──▶  route functions

Settings for Several Environments

Projects usually run in three places: development, testing, and production. Keep one class and change the values.

  • Development uses a local SQLite file and debug turned on.
  • Testing points to a temporary database.
  • Production reads secrets from the hosting platform and keeps debug off.

Common Mistakes

Committing the .env File

A leaked secret key lets strangers forge login tokens. Rotate any key that reached a public repository.

Printing Secrets in Logs

Log the app name and mode. Never log passwords, keys, or full database URLs.

Forgetting to Restart

The server reads settings at startup. Restart after editing the .env file.

A Day in the Life of a Setting

Follow one setting through a project. A developer named Ravi needs a database address. On his laptop the value points to a local SQLite file. A teammate named Meera uses the same code with a PostgreSQL server on her machine. Neither person edits Python files, because each keeps a private .env file.

The deployment server uses neither file. The hosting platform stores the production address as a protected variable and hands it to the process at start. The code stays identical in all three places. Only the surrounding values change.

Such a setup speeds up onboarding. A new teammate copies .env.example, fills in a few values, and runs the app within minutes.

Key Points

  • Settings keep environment-specific values out of the code.
  • BaseSettings reads environment variables and .env files.
  • Fields without defaults act as required settings.
  • Real environment variables beat the file, and the file beats class defaults.
  • Never commit secrets to version control.