Nested settings models in pydantic
Flat configuration becomes unreadable once you have CACHE_HOST, CACHE_PORT, CACHE_SSL, DB_HOST, DB_PORT. Nested settings models group related fields into sub-objects while still reading from flat environment variables. This page builds them, extending Pydantic Settings Fundamentals.
Problem 1: a flat soup of prefixed fields
# ANTI-PATTERN: related fields scattered flat
class Settings(BaseSettings):
cache_host: str
cache_port: int
cache_ssl: bool
db_host: str
db_port: int # no grouping, no reuse
There is no structure and no way to pass “the cache config” as one object.
Problem 2: wrong delimiter, empty sub-model
# ANTI-PATTERN: single underscore collides with field names
model_config = SettingsConfigDict(env_nested_delimiter="_") # CACHE_HOST ambiguous
A single-underscore delimiter clashes with normal field names; the sub-model ends up empty or mis-parsed.
Secure implementation
# config/nested.py
from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict
class CacheConfig(BaseModel):
host: str
port: int = 6379
ssl: bool = True
class DBConfig(BaseModel):
host: str
port: int = 5432
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_nested_delimiter="__", # double underscore avoids field-name clashes
extra="forbid",
)
cache: CacheConfig
db: DBConfig
# Env: CACHE__HOST=redis.local CACHE__SSL=true DB__HOST=pg.local
settings = Settings()
settings.cache.ssl # typed bool, grouped under a reusable sub-model
env_nested_delimiter="__" maps CACHE__HOST to cache.host. The same flat variables that Kubernetes injects populate structured, reusable sub-models — exactly the format used in YAML config.
Gotchas & version-specific behaviour
- Use
__(double underscore) as the delimiter so it never collides with field names. - Sub-models are plain
BaseModel, notBaseSettings. - Defaults on sub-model fields work normally; required sub-fields raise if unset.
- The same
CACHE__HOSTform works locally in a.envand in Kubernetes secrets — full parity.
Production parity checklist
- Related fields grouped into
BaseModelsub-models. env_nested_delimiter="__"set; variables use the double-underscore form.extra="forbid"rejects unexpected nested keys.- Local
.envuses the samePARENT__CHILDkeys as production injection. - Required sub-fields validated at startup.
Conclusion
Nested sub-models give structure and reuse while reading the same flat variables every platform injects. For nested file config, see Handling Nested Configuration in YAML Safely.