pydantic-settings review
pydantic-settings 2.15.0 builds a validated Pydantic model from command-line arguments, constructor values, environment variables, dotenv files, mounted secret files, config files, or optional cloud secret sources. It handles source precedence and naming, while Pydantic handles field parsing and validation. Release 2.15.0 makes case_sensitive apply to constructor and JSON, TOML, and YAML inputs; adds PYDANTIC_SETTINGS_DEBUG; supports nested TOML table roots and packaged config resources; and shows environment names in generated CLI help. It also warns on unresolved forward references and fixes UTF-8 secret reads plus nested-secret symlink containment. Our Python 3.12 import passed and py.typed was present.
pydantic-settings 2.15.0 installed in 0.4 seconds, used 8 MB across 7 packages, imported in 0.67 seconds, and returned 0 pip-audit findings in our sandbox. It earns its place when a Pydantic application needs validated startup configuration from several sources; direct environment reads are clearer for a few scalar values, and PYDANTIC_SETTINGS_DEBUG must stay away from production logs.
We installed it
| Install | ✓ · 0.4s | 7 packages on disk · 8 MB |
| Import | ✓ | import pydantic_settings in 0.67s · pure Python · py.typed · requires Python >=3.10 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does pydantic-settings install cleanly?
Yes. In a fresh container with an empty cache, pip install pydantic-settings finished in 0.4s, leaving 7 packages and 8 MB on disk. pip-audit reported no known vulnerabilities.
What does pydantic-settings need to run?
Python >=3.10, and nothing compiled: it is pure Python. In our run import pydantic_settings succeeded in 0.67s, and the package ships py.typed for type checkers.
pydantic-settings or environs: which should you use?
environs: Use it for a smaller Marshmallow-based API centered on environment and dotenv values. pydantic-settings 2.15.0 installed in 0.4 seconds, used 8 MB across 7 packages, imported in 0.67 seconds, and returned 0 pip-audit findings in our sandbox.
When should you not use pydantic-settings?
The program reads three scalar environment values and otherwise has no Pydantic dependency. os.environ with explicit conversions is easier to audit.
Use it if
- The application already uses Pydantic and should fail startup when a required setting is missing or has the wrong shape.
- Local dotenv files, deployment environment variables, and mounted secret files need one documented priority order.
- Nested models should map to names such as APP_DATABASE__HOST without a separate configuration object.
- A typed settings model should also drive command-line flags, TOML or YAML input, or a cloud secret source.
- The program reads three scalar environment values and otherwise has no Pydantic dependency. os.environ with explicit conversions is easier to audit.
- Settings must refresh after process startup. BaseSettings reads its sources when an instance is constructed and does not watch files or remote stores.
- Operators require comma-separated lists with no custom code. Complex environment values use JSON by default, so 1,2,3 needs NoDecode plus a validator or custom source.
- Configuration should merge named environments across many files with built-in live reload. Dynaconf is designed around that workflow, while pydantic-settings expects you to assemble sources.
- Secret values may never appear in debug logs. Version 2.15's PYDANTIC_SETTINGS_DEBUG output includes each source dictionary, which can contain environment, dotenv, and secret-file values.
Setup reality
We installed pydantic-settings 2.15.0 without a cache in an unprivileged Python 3.12 Bookworm container. It completed in 0.4 seconds and left 7 packages using 8 MB. The measured package metadata listed 9 direct dependencies, Python 3.10 or newer, pure Python code, py.typed, and the MIT License. pip-audit found 0 known vulnerabilities. import pydantic_settings succeeded in 0.67 seconds. Pydantic and python-dotenv are part of this base footprint.
Default priority runs from CLI arguments, when enabled, to constructor values, environment variables, dotenv, secret files, then model defaults. Real environment values beat .env values. A relative env_file starts at the current working directory; env_file_depth can search a configured number of parent levels. Complex fields expect JSON text. Aliases and prefixes change the names every source searches, and an unmatched dotenv key can raise ValidationError under the default extra='forbid'.
Version 2.15 adds PYDANTIC_SETTINGS_DEBUG=1. With DEBUG logging enabled, it prints every source in priority order, the values each source returned, which value won, and which files were checked. Those dictionaries may contain passwords and tokens, so use the flag only in a trusted debugging session and turn it off afterward. settings_customise_sources() can reorder or replace sources; the returned tuple runs from highest to lowest priority, so a wrong order silently changes configuration.
A secrets directory maps filenames to field values. Version 2.15 reads them as UTF-8 and blocks NestedSecretsSettingsSource symlinks that point outside secrets_dir. AWS, Azure, Google, TOML, and YAML sources require extras beyond our 7-package base install, and cloud sources may perform network I/O while constructing settings. Build one settings instance during startup or cache it instead of repeating file and network work for every request. case_sensitive now affects constructor and config-file inputs too, so mixed-case keys that were ignored before 2.15 may start populating fields.
Patterns
Validate startup environment values load-environment
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix='APP_')
database_url: str
workers: int = 4
debug: bool = False
settings = Settings()APP_DATABASE_URL is required. Pydantic converts APP_WORKERS and APP_DEBUG, then raises ValidationError if conversion fails.
Read a dotenv file with an explicit encoding load-dotenv
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file='.env',
env_file_encoding='utf-8',
extra='ignore',
)
api_url: str
settings = Settings()Real environment variables override .env values. extra='ignore' prevents unrelated dotenv keys from failing model construction.
Search parent directories for .env search-parent-dotenv
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file='.env',
env_file_depth=2,
)
database_url: strenv_file_depth=2 checks at most 2 parent levels when the relative file is absent from the current working directory.
Map nested environment names load-nested-model
from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict
class Database(BaseModel):
host: str
port: int = 5432
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix='APP_',
env_nested_delimiter='__',
)
database: Database
# APP_DATABASE__HOST=db.internalThe double separator turns APP_DATABASE__HOST into database.host. Unknown field paths are not expanded.
Accept a comma-separated environment list parse-comma-list
from typing import Annotated, Any
from pydantic import BeforeValidator
from pydantic_settings import BaseSettings, NoDecode
def split_csv(value: Any) -> Any:
return value.split(',') if isinstance(value, str) else value
CsvList = Annotated[list[str], NoDecode, BeforeValidator(split_csv)]
class Settings(BaseSettings):
allowed_hosts: CsvListNoDecode stops the default JSON parser. Without it, a value such as api.local,admin.local is invalid JSON.
Read mounted secret files load-docker-secrets
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(secrets_dir='/run/secrets')
database_password: str
settings = Settings()/run/secrets/database_password supplies the field. Environment and dotenv values have higher default priority than secret files.
Let environment beat constructor values change-source-priority
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
class Settings(BaseSettings):
endpoint: str
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
):
return env_settings, init_settings, dotenv_settings, file_secret_settingsThe tuple is ordered from highest to lowest priority. This example intentionally lets an environment value replace the same constructor keyword.
Trace which source supplied each value debug-source-resolution
import logging
import os
logging.basicConfig(level=logging.DEBUG)
os.environ['PYDANTIC_SETTINGS_DEBUG'] = '1'
settings = Settings()Version 2.15 logs each source dictionary and winning values. The output may include secrets, so do not leave this enabled in production.
Turn settings fields into command-line flags generate-cli
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
cli_parse_args=True,
cli_show_env_vars=True,
env_prefix='APP_',
)
host: str = '127.0.0.1'
port: int = 8000
settings = Settings()CLI arguments are the highest-priority source by default. Version 2.15 can show the corresponding environment names in help output.
Use one TOML table as the settings root load-toml-table
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
TomlConfigSettingsSource,
)
class Settings(BaseSettings):
model_config = SettingsConfigDict(
toml_file='config.toml',
toml_table_header=('service',),
)
port: int
@classmethod
def settings_customise_sources(cls, settings_cls, *sources):
return (TomlConfigSettingsSource(settings_cls),)Version 2.15 lets TomlConfigSettingsSource root values at [service] instead of reading the whole file as the model.
Add AWS Secrets Manager as a source load-aws-secret
from pydantic_settings import (
AWSSecretsManagerSettingsSource,
BaseSettings,
)
class Settings(BaseSettings):
database_password: str
@classmethod
def settings_customise_sources(cls, settings_cls, *sources):
aws = AWSSecretsManagerSettingsSource(
settings_cls,
secret_id='prod/my-service',
)
return (*sources, aws)Install the aws-secrets-manager extra and provide normal AWS credentials. This source can make a network request during Settings construction.
Create one settings instance per process cache-settings
from functools import lru_cache
@lru_cache
def get_settings() -> Settings:
return Settings()
settings = get_settings()BaseSettings reads its sources during construction. Caching avoids repeated dotenv, secret-file, and optional cloud-source work on every request.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| environs | PyPI | Use it for a smaller Marshmallow-based API centered on environment and dotenv values. |
| dynaconf | PyPI | Use it when named environments, layered file merging, remote stores, and reload behavior define the configuration system. |
| python-decouple | PyPI | Use it for a compact Django-friendly split between environment values, local config files, and code defaults. |
More utils guides
lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

