Skip to content

Settings Management Interview Questions & Answers

12 questions Updated 2026-06-20 Share:

FastAPI Pydantic settings interview questions — BaseSettings, env vars, .env files, secrets, nested settings and caching with lru_cache.

12 of 12

BaseSettings (from pydantic-settings) is a BaseModel subclass that reads field values from environment variables automatically. It gives you typed, validated configuration without manual os.getenv() calls.

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    secret_key: str
    debug: bool = False
    max_connections: int = 10

settings = Settings()
# reads DATABASE_URL, SECRET_KEY, DEBUG, MAX_CONNECTIONS from env

Benefits over raw os.getenv():

  • Pydantic validates types and raises a clear error at startup if required vars are missing.
  • Settings are documented as a class — easy to audit.
  • Works with .env files, Docker secrets, and AWS Parameter Store.

Rule of thumb: put all configuration in a BaseSettings class; never scatter os.getenv() calls across the codebase.

Configure model_config with env_file:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}

    database_url: str
    secret_key: str
    debug: bool = False

.env file:

DATABASE_URL=postgresql+asyncpg://user:pass@localhost/mydb
SECRET_KEY=supersecret
DEBUG=true

Multiple .env files (later overrides earlier):

model_config = {"env_file": (".env", ".env.local")}

Rule of thumb: commit .env.example with dummy values and add .env to .gitignore — never commit real secrets to version control.

Wrap the Settings() constructor in @lru_cache and inject it via Depends:

from functools import lru_cache
from fastapi import Depends

@lru_cache
def get_settings() -> Settings:
    return Settings()   # reads env/file once; cached for process lifetime

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

@lru_cache makes get_settings() a singleton: the first call creates the object; subsequent calls return the same instance. Safe because environment variables don't change during a process lifetime.

Rule of thumb: always wrap settings construction in @lru_cache — reading env vars is cheap, but parsing and validating with Pydantic on every request adds unnecessary overhead.

By default case-insensitive on all platforms. DATABASE_URL, database_url, and Database_Url all resolve to the database_url field.

class Settings(BaseSettings):
    database_url: str   # matches DATABASE_URL, database_url, etc.

To enforce case-sensitive env var names:

model_config = {"case_sensitive": True}

Rule of thumb: use UPPERCASE for environment variable names by convention (Linux, 12-factor apps); keep field names lowercase — the case-insensitive matching bridges them automatically.

Use nested Pydantic models. BaseSettings reads nested values via a delimiter prefix in the env var name:

from pydantic import BaseModel
from pydantic_settings import BaseSettings

class DatabaseSettings(BaseModel):
    url: str
    pool_size: int = 5

class Settings(BaseSettings):
    model_config = {"env_nested_delimiter": "__"}
    database: DatabaseSettings
    debug: bool = False

# env vars: DATABASE__URL=postgres://..., DATABASE__POOL_SIZE=10

Rule of thumb: use env_nested_delimiter="__" for nested settings — it's the conventional double-underscore pattern in 12-factor apps.

Point secrets_dir to the directory where secret files live. Each file named after the env var contains the secret value.

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    model_config = {"secrets_dir": "/run/secrets"}
    database_password: str   # reads /run/secrets/database_password
    api_key: str             # reads /run/secrets/api_key

Docker Swarm and Kubernetes both mount secrets as files at a known path. This approach keeps secrets out of environment variables (less visible in ps output and container inspection).

Rule of thumb: prefer secrets-as-files over env var secrets in containerised deployments — they integrate cleanly with Kubernetes Secrets and Docker secrets.

Override the get_settings dependency on the test app:

from fastapi.testclient import TestClient
from app.main import app
from app.config import get_settings, Settings

def get_test_settings():
    return Settings(
        database_url="sqlite:///:memory:",
        secret_key="test-secret",
        debug=True,
    )

app.dependency_overrides[get_settings] = get_test_settings
client = TestClient(app)

Alternatively, set environment variables before the settings are loaded:

import os
os.environ["DATABASE_URL"] = "sqlite:///:memory:"

Rule of thumb: use dependency_overrides — it's explicit, isolated per test file, and doesn't pollute os.environ for other tests.

Yes — BaseSettings inherits from BaseModel, so @field_validator and @model_validator work identically:

from pydantic import field_validator
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    allowed_hosts: list[str] = ["*"]
    cors_origins: str = ""

    @field_validator("cors_origins", mode="before")
    @classmethod
    def parse_cors(cls, v: str) -> list[str]:
        return [origin.strip() for origin in v.split(",") if origin.strip()]

This pattern lets you store a comma-separated env var and parse it into a list.

Rule of thumb: use @field_validator(mode="before") in BaseSettings to parse compound env vars (comma-separated lists, JSON strings) into the target type.

A module-level singleton:

# config.py
settings = Settings()   # created once at import time

This works in production but breaks in tests: if os.environ is patched after import, the singleton already holds the old values.

The Depends + @lru_cache pattern is safer:

@lru_cache
def get_settings() -> Settings:
    return Settings()

# In tests:
app.dependency_overrides[get_settings] = lambda: Settings(debug=True)

The lru_cache singleton is invalidated between tests by clearing the cache:

get_settings.cache_clear()

Rule of thumb: use Depends(get_settings) with @lru_cache for testability; avoid module-level settings = Settings() in anything you'll need to test.

All Pydantic-supported types work. Pydantic coerces the string value from the env var to the declared type:

Python type Env var example
str SECRET_KEY=abc
int PORT=8000
float TIMEOUT=30.5
bool DEBUG=true / DEBUG=1
list[str] TAGS=["a","b"] (JSON) or use @field_validator
HttpUrl BASE_URL=https://example.com
SecretStr PASSWORD=secret (masked in repr)
from pydantic import SecretStr, HttpUrl

class Settings(BaseSettings):
    database_password: SecretStr    # hidden in logs
    api_base_url: HttpUrl           # validated URL

Rule of thumb: use SecretStr for passwords and tokens — it masks the value in repr() and str(), preventing accidental logging.

Load different .env files based on an APP_ENV env var:

import os
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    model_config = {
        "env_file": f".env.{os.getenv('APP_ENV', 'development')}"
    }
    database_url: str
    debug: bool = False

File layout:

.env.development   → local dev DB, DEBUG=true
.env.staging       → staging DB, DEBUG=false
.env.production    → prod secrets (not committed)

In CI/CD and production, inject all values directly as environment variables rather than relying on files.

Rule of thumb: in production, always prefer environment variables over .env files — files can be accidentally committed or left on disk.

Since Pydantic v2, BaseSettings has been moved to a separate package: pydantic-settings. It must be installed separately:

pip install pydantic-settings

Import:

from pydantic_settings import BaseSettings

In Pydantic v1, BaseSettings was part of pydantic itself (from pydantic import BaseSettings) — a common migration mistake is forgetting to install pydantic-settings after upgrading.

Rule of thumb: add pydantic-settings to requirements.txt or pyproject.toml explicitly — it is not pulled in by fastapi or pydantic alone.

More ways to practice

The self-quiz is live. Get notified when mock interviews and new question packs drop.

or
Join our WhatsApp Channel