pydantic-settingsは、Pydanticのモデルと同じ書き方で設定クラスを定義し、環境変数と.envファイルから値を読み込んで型を検証するライブラリです。BaseSettingsを継承したクラスにport: intと書けば、環境変数の文字列"8000"は整数に変換され、"abc"なら起動の時点でValidationErrorになります。この記事でコードを示す範囲は、最新版2.16.3(2026年11月時点)でDB接続情報とAPIキーを持つ設定クラスを作り、FastAPIへDependsで注入するまでの手順です。共用の.envで起きる「Extra inputs are not permitted」の対処や、SecretStrでパスワードをログに出さない書き方も扱います。
まとめ:pydantic-settingsで設定を読むときの結論
設定値が5つを超え、環境ごとに値が変わるアプリなら、os.environを散らさずBaseSettingsのクラス1つへ集めます。型と必須項目が起動時に検証され、環境変数の付け忘れをデプロイの段階で止められます。
読み込みの優先順位は「初期化引数>環境変数>.env>secretsのファイル>既定値」です。.envを他のツールと共用するならenv_prefixを付け、dotenv_filtering="match_prefix"で自分の変数だけを読ませます。パスワードとAPIキーはSecretStrで受け、本番ではファイルかクラウドの保管庫から渡します。
FastAPIでは設定クラスをDependsで注入し、settings_cached()かlru_cacheで読み込みを1回に絞ります。
pydantic-settings 2.16の導入と環境変数を読む最小コード
まずはインストールと、環境変数を1つ読むだけの最小の設定クラスで動きを確かめます。
pydantic-settingsの導入とPython 3.11以上の動作要件
PyPIのpydantic-settingsでは、2026年11月14日時点の最新が2.16.3(2026年11月13日公開)です。2.16.0からPython 3.11以上が必要になり、3.10の環境では2.15系までしか入りません。依存としてpydantic2.7.0以上とpython-dotenv1.2.2以上が一緒に入るため、.envを読むために別のパッケージを足す必要はありません。
# 仮想環境を作り、pydantic-settings を入れる
python3 -m venv .venv
source .venv/bin/activate
pip install "pydantic-settings==2.16.3"
# 入った版を確かめる
python -c "import pydantic_settings; print(pydantic_settings.__version__)"
pydantic本体のv1時代はfrom pydantic import BaseSettingsで使えましたが、v2で別パッケージに分かれました。古い記事のコードをそのまま貼るとPydanticImportErrorになります。Pydanticそのものの位置付けとv1との違いは、Pydanticの読み方・BaseModelの使い方とV1との違いで整理しています。
BaseSettingsを継承した設定クラスと型変換・必須項目の検証
次のファイルをconfig.pyとして置きます。
# config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "sample-api"
port: int = 8000
debug: bool = False
admin_email: str # 既定値なし=必須
settings = Settings()
print(settings.model_dump())
# 必須の admin_email が無いと起動時に止まる
python config.py
# pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
# admin_email
# Field required ...
# 環境変数で渡すと、文字列が型に合わせて変換される
[email protected] PORT=9000 DEBUG=true python config.py
# {'app_name': 'sample-api', 'port': 9000, 'debug': True, 'admin_email': '[email protected]'}
環境変数名はフィールド名と同じで、既定では大文字と小文字を区別しません。PORT=abcと渡せばintに変換できず、ValidationErrorで止まる仕組みです。既定値も検証されるため、既定値の型の誤りも起動時に見つかります。
.envと環境変数を読む優先順位と複数ファイル・共用.envの扱い方
同じ項目が複数の場所に書かれていたとき、どれが勝つかを先に押さえておくと、「設定したのに反映されない」の大半は自分で切り分けられます。
初期化引数・環境変数・.env・secrets・既定値の優先順位
公式のSettings Managementが定める優先順位は、上ほど強い次の6段です。
- CLI引数(
cli_parse_argsを有効にしたときだけ) Settings(port=9000)のような初期化引数- 環境変数
.envファイルの値secrets_dirに置いたファイルの値- クラスに書いた既定値
実務で効いてくるのは3と4の関係です。シェルにexport DEBUG=falseが残っていると、.envにDEBUG=trueと書いてもfalseのまま動きます。.envを書き換えても反映されないときは、まずenv | grep -i debugで同名の環境変数が無いかを見ます。
env_fileに.envと.env.prodを渡したときの後勝ちの仕組み
.envを読むにはmodel_configにenv_fileを書きます。タプルで複数渡すと前から順に読み、同じキーは後のファイルの値で上書きされる仕組みです。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
# 共通の .env を読んだあと、手元だけの .env.local で上書きする
env_file=(".env", ".env.local"),
env_file_encoding="utf-8",
)
port: int = 8000
debug: bool = False
無いファイルはエラーにならず読み飛ばされるため、.env.localは開発者の手元にだけ置けば済みます。本番で.envを読ませたくない場合は、Settings(_env_file=None)のように初期化時にNoneを渡します。相対パスは起動したディレクトリから探すため、2.16.0では親ディレクトリまでさかのぼるenv_file_depth(既定は0)が加わりました。.envそのものの記法や、Gitに載せない運用はdotenvの書き方と読み込み方の解説に譲ります。
共用の.envで発生する余分な入力エラーの原因と設定による対処
Docker Composeと同じ.envを使っていると、次のエラーで起動しないことがあります。
pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
postgres_password
Extra inputs are not permitted [type=extra_forbidden, input_value='local-pass', input_type=str]
原因は2つの既定値の組み合わせです。BaseSettingsのextraは"forbid"で、2.16.3のソースでは、.envのエントリはフィールドに当たらないものも含めて全部モデルに渡されます。Compose用のPOSTGRES_PASSWORDがフィールドに無いため、余計な入力として弾かれる仕組みです。環境変数の側はフィールドに当たらないものを無視するので、同じ値をexportしたときは起きません。
対処は3通りあり、おすすめは上から順です。
| 設定 | .envの余計なキー | 自分の変数の打ち間違い |
|---|---|---|
| env_prefix+match_prefix | 無視する | エラーで気付ける |
| only_existing指定 | 無視する | 黙って無視される |
| extra=”ignore” | 無視する | 黙って無視される |
env_prefix="APP_"とdotenv_filtering="match_prefix"を組み合わせると、APP_で始まるキーだけがモデルに渡ります。APP_DEBGU=trueのような打ち間違いは余計な入力として止まり、POSTGRES_PASSWORDは無視されます。extra="ignore"は手軽ですが、打ち間違えた変数名が既定値のまま黙って動くため、本番でdebugが効いていないことに気付けません。extraの各値の挙動はConfigDictの設定一覧で比較しています。
env_prefixとenv_nested_delimiterによるネスト設定
設定が増えてくると、DBやメールなどの単位でまとめたくなります。ここではアプリ全体の設定クラスを、記事の後半まで使う形で組み立てます。
APP_のprefixとDATABASE__HOSTでサブモデルへ値を入れる設定
# config.py
from pydantic import BaseModel, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class DatabaseSettings(BaseModel):
host: str = "localhost"
port: int = 5432
name: str = "app"
user: str = "app"
password: SecretStr
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="APP_",
env_nested_delimiter="__",
env_file=(".env", ".env.local"),
env_file_encoding="utf-8",
dotenv_filtering="match_prefix",
)
debug: bool = False
allowed_origins: list[str] = ["http://localhost:3000"]
database: DatabaseSettings
openai_api_key: SecretStr
# .env(Docker Compose と共用。APP_ で始まらない行は読まれない)
POSTGRES_PASSWORD=local-pass
APP_DEBUG=true
APP_ALLOWED_ORIGINS=["https://example.com","https://admin.example.com"]
APP_DATABASE__HOST=db
APP_DATABASE__PASSWORD=local-pass
APP_OPENAI_API_KEY=sk-local-dummy
env_nested_delimiter="__"を指定すると、APP_DATABASE__HOSTはdatabase.hostに入ります。区切りを1文字の_にすると、openai_api_keyのようにフィールド名自体に含まれる_まで分割されてしまうため、2文字の__にするのが定石です。
env_prefixは既定でフィールド名から作る変数名にだけ付き、validation_aliasで別名を付けたフィールドには付きません。別名にも付けたいときはenv_prefix_target="all"を指定します。
alias・validation_alias・serialization_alias の3種の違いは「Pydantic Fieldの使い方:default_factory・alias・制約の引数一覧とOpenAPIへの反映」で解説しています。
listとdictはJSON文字列で渡すルールと空文字の扱いの違い
上のAPP_ALLOWED_ORIGINSのように、list・dict・サブモデルの型を持つフィールドは、環境変数の値をJSONとして解釈します。APP_ALLOWED_ORIGINS=https://example.com,https://admin.example.comとカンマ区切りで書くとJSONとして読めず、SettingsErrorで起動に失敗します。カンマ区切りで渡したい事情があるなら、フィールドをstrで受けてfield_validatorで分割する方が素直です。
空文字の扱いも、設定を読み込む際の落とし穴です。APP_PORT=のように値が空の行は、既定(env_ignore_empty=False)では空文字として渡され、intのフィールドならValidationErrorになります。テンプレートの.env.exampleを値を空のまま複製する運用なら、env_ignore_empty=Trueにして空の行を「未設定」として扱わせます。
SecretStrとsecrets_dirでパスワード・APIキーをログに出さない実装
設定クラスをそのままprintやログに出すと、パスワードまで平文で残ります。ここでは表示を伏せる型と、ファイルから渡す仕組みを組み合わせます。
SecretStrで表示を伏せget_secret_valueで取り出す使い方
先ほどの設定クラスでpasswordとopenai_api_keyをSecretStrにしたのは、このためです。PydanticのTypesのAPIに記載のとおり、SecretStrはstr()とrepr()で値を**********に置き換えます。
from config import Settings
settings = Settings()
print(settings.openai_api_key)
# **********
print(settings.database)
# host='db' port=5432 name='app' user='app' password=SecretStr('**********')
# 実際に使う場所でだけ元の値を取り出す
api_key = settings.openai_api_key.get_secret_value()
伏せられるのは表示とJSON出力までで、get_secret_value()で取り出した後は普通のstrに戻ります。取り出すのはSDKのクライアントを作る1か所に限り、変数に入れて持ち回らないようにします。
Docker secretsのファイルをsecrets_dirで読み込む設定と注意
本番では、パスワードを環境変数に置かずファイルで渡す構成もよく使います。環境変数はdocker inspectや子プロセスから見えるためです。Docker Composeのsecretsは、指定した値をコンテナ内の/run/secrets/以下にファイルとして置きます。
# compose.yaml
services:
api:
build: .
secrets:
- app_openai_api_key
secrets:
app_openai_api_key:
file: ./secrets/openai_api_key.txt
設定クラスのmodel_configにsecrets_dir="/run/secrets"を足すと、ファイル名をキー、中身を値として読みます。ファイル名にはenv_prefixが付くため、上の例で使うファイル名はapp_openai_api_keyです。ディレクトリが無い環境では警告が出るだけで止まらないため、開発機と本番で同じクラスを使えます。
2.16.3(2026年11月13日)では、secrets_dirの外を指すシンボリックリンクを追わないよう修正されました(v2.16.3のリリースノート)。ネストしたsecretsを読むNestedSecretsSettingsSourceは2.14.2で同種の問題が直っています。secrets_dirを使うなら2.16.3以上に上げておきます。
AWS Secrets Managerなどクラウドの保管庫から読む拡張の選び方
pydantic-settingsには、クラウドの保管庫から値を読むソースも同梱されています。使うには、PyPIに登録されたextraを付けてインストールします。次の表のGCPはGoogle Cloudの略で、対象の保管庫はGoogle Cloud Secret Managerです。
| 保管庫 | インストールするextra |
|---|---|
| AWS Secrets Manager | aws-secrets-manager |
| AWS SSM Parameter Store | aws-systems-manager |
| Azure Key Vault | azure-key-vault |
| GCP Secret Manager | gcp-secret-manager |
これらは既定の読み込み順に入っていないため、settings_customise_sourcesを上書きし、返すタプルに追加します(先頭ほど優先)。起動時に1回読んだ値は変わらないので、保管庫側でパスワードを自動ローテーションするなら再起動を伴う運用にします。料金とローテーションの設定はAWS Secrets Managerの料金とローテーション設定で扱っています。
FastAPIで設定クラスをDependsから注入しテストで差し替える構成
FastAPI公式のSettings and Environment Variablesは、設定をモジュールの変数に置かず、依存関数から渡す形を勧めています。FastAPI 0.162.7のfastapi[standard]にはpydantic-settingsも含まれますが、指定は2.0.0以上と緩いため、ここまでの機能を使うならpydantic-settings>=2.16.3を別に固定します。
lru_cacheとsettings_cachedで設定の読み込みを1回にする書き方
Settings()は呼ぶたびに環境変数と.env、secretsのファイルを読み直して検証します。リクエストごとに作るとディスクの読み込みが毎回走るため、1回だけ作って使い回します。公式の例は@lru_cacheを付けた関数ですが、2.16.0で同じ役割のsettings_cached()がBaseSettingsに加わりました(v2.16.0のリリースノート)。
# main.py
from typing import Annotated
from fastapi import Depends, FastAPI
from config import Settings
app = FastAPI()
def get_settings() -> Settings:
# 初回だけ読み込み、以降は同じインスタンスを返す(2.16.0以上)
return Settings.settings_cached()
SettingsDep = Annotated[Settings, Depends(get_settings)]
@app.get("/info")
def info(settings: SettingsDep):
return {"debug": settings.debug, "db_host": settings.database.host}
settings_cached()はクラスごとにキャッシュを持ち、settings_clear_cache()を呼ぶと次の呼び出しで読み直します。2.15系以前の環境で同じ動きにする方法は、get_settingsに@lru_cacheを付けてreturn Settings()と書く形です。Dependsそのものの仕組みと、Annotatedで型エイリアスにする書き方はFastAPIのDependsの使い方で詳しく扱っています。
キャッシュはワーカーのプロセスごとに1つです。--workers 4なら4回読み込まれ、どこかで値を書き換えても他のワーカーには伝わりません。DBの接続プールのように、設定から作ってアプリ全体で共有する資源はFastAPIのlifespanでの起動処理で作ります。
dependency_overridesでテスト用の設定へ差し替える手順
エンドポイントがget_settings経由で設定を受け取っていれば、テストでは依存関数ごと差し替えられます。
# test_main.py
from fastapi.testclient import TestClient
from config import DatabaseSettings, Settings
from main import app, get_settings
def get_test_settings() -> Settings:
# .env を読まず、テストに必要な値だけを初期化引数で渡す
return Settings(
_env_file=None,
debug=True,
database=DatabaseSettings(host="test-db", password="test"),
openai_api_key="sk-test",
)
def test_info():
app.dependency_overrides[get_settings] = get_test_settings
try:
client = TestClient(app)
res = client.get("/info")
assert res.json() == {"debug": True, "db_host": "test-db"}
finally:
app.dependency_overrides.clear()
_env_file=Noneは、手元の.envでテスト結果が変わるのを防ぐためです。初期化引数は環境変数より優先されるので、CIのランナーにAPP_DEBUGが残っていても、テストで渡した値が勝ちます。
pydantic-settingsを採用する条件と使わずに済ませる場面の判断
便利なライブラリですが、すべての設定読み込みに入れる必要はありません。判断の軸は「設定の数」と「型の誤りが起きたときの損害」です。
pydantic-settingsを使わずos.environで足りる場面と移行の目安
読む値が1〜2個の使い捨てスクリプトや、AWS Lambdaの小さな関数なら、os.environ["TABLE_NAME"]で十分です。依存の追加と、2.16.0のようなPython対応版の引き上げに追従する手間の方が重くなります。
次のどれかに当てはまったら、BaseSettingsへ移す時期です。
- 設定値が5つを超え、
intやboolへの変換をあちこちで手書きしている - 開発・ステージング・本番で
.envや環境変数を切り替えている - APIキーやDBのパスワードを扱い、ログへの漏れを防ぎたい
- FastAPIで、テストのたびに設定を差し替えたい
最優先は1つ目です。os.environ.get("DEBUG") == "true"のような比較が3か所に散ると、どこか1か所だけ"True"や"1"を受け付けない、という不一致が必ず起きます。既存のFlaskやDjangoのAPIをFastAPIへ移しながら、設定の持ち方やシークレットの渡し方も整理したい場合は、API開発・システム連携の受託開発でご相談を受け付けています。
実行中の設定変更やフロントエンドとの設定共有で失敗するパターン
pydantic-settingsが向かないのは、実行中に設定を変えたい場面です。機能フラグのように運用中に切り替える値はDBや機能フラグのサービスに置き、pydantic-settingsにはその接続先だけを持たせます。
もう1つは、フロントエンドや他言語のサービスと同じ設定ファイルを共有する場面です。env_nested_delimiterの__やJSONで書くlistは、Node.jsのdotenvにとってはただの文字列です。共有するのは接続先のURLのような単純な値に留め、型付きの構造はPython側の設定クラスで組み立てます。言語をまたいだ環境変数の受け渡しは環境変数の設定方法とDocker・CIでの受け渡しで整理しています。
よくある質問
pydantic-settingsの導入時に出てくることの多い質問に、2026年11月時点の版(pydantic-settings 2.16.3、pydantic 2.14.3)で答えます。
from pydantic import BaseSettings でエラーになるのはなぜですか?
Pydantic v2でBaseSettingsが本体から外れ、pydantic-settingsという別パッケージに移ったためです。pydantic 2.14.3で古い書き方をすると、PydanticImportErrorで「BaseSettings has been moved to the pydantic-settings package」と表示されます。インストールにはpip install pydantic-settingsを使い、インポート文の変更先はfrom pydantic_settings import BaseSettingsです。v1のclass Configで書いた設定は、model_config = SettingsConfigDict(...)に移します。
環境変数名の大文字と小文字は区別されますか?
既定(case_sensitive=False)では区別しません。PORTでもportでも同じフィールドに入ります。2.15.0からは、この設定が初期化引数と設定ファイル(JSON・TOML・YAML)にも効くようになりました。区別したい場合はcase_sensitive=Trueにしますが、Windowsの環境変数には効きません。
pydantic-settingsとpython-dotenvはどう使い分けますか?
python-dotenvは.envの中身を環境変数へ流し込むだけで、型の変換も必須項目の確認もしません。pydantic-settingsは内部でpython-dotenvを使って.envを読み、その上で型を検証します。Pythonのアプリなら、load_dotenv()を呼ぶ代わりにenv_fileを指定すれば足り、両方を呼ぶ必要はありません。
.envを書き換えたら実行中のアプリに反映されますか?
反映されません。設定クラスのインスタンスが持ち続けるのは、そのインスタンスを作った時点で読み込んだ値です。settings_cached()を使っているならSettings.settings_clear_cache()を呼んだ後の呼び出しで読み直しますが、ワーカーが複数あれば各プロセスで呼ぶ必要があります。開発中はfastapi devや--reloadの自動再起動に任せ、本番では値の変更をデプロイか再起動とセットにします。
既定値にも型のバリデーションは掛かりますか?
はい、既定値も型のバリデーションの対象です。BaseModelと違い、BaseSettingsはvalidate_default=Trueが既定で、port: int = "abc"のような誤りは起動時にエラーになります。止めたい場合は、model_configにvalidate_default=Falseを書くか、フィールドごとにField(validate_default=False)を指定します。
関連記事
- Pydanticとは?読み方・BaseModelの使い方とV1との違い【2.14対応】:BaseSettingsの土台になるPydanticの入門です。
- Pydantic v2のConfigDict設定一覧|model_configの書き方とv1からの移行【2.14対応】:extraやstrictなど設定項目ごとの挙動です。
- dotenvとは?.envの書き方と読み込み方、dotenv 18・Node.js・Python・Railsの使い方:.envの記法と言語ごとの読み込み方です。
- FastAPIのDependsの使い方|依存性注入・yield・テストの差し替えをコードで解説:設定の注入に使う依存性注入の仕組みです。
- FastAPIのlifespanの使い方|on_eventからの移行とDB接続・モデル読込をコードで解説:設定から作る接続プールの置き場所です。