pip install dynaconf[yaml],[ini],[vault],[redis]extras for those sources.from dynaconf import Dynaconf★The one import.settings = Dynaconf(...).Dynaconf(settings_files=[…])★List of files to load, in order. First-format wins ties.Dynaconf(environments=True)★Enable[default]/[dev]/[prod]sections (off by default).Dynaconf(envvar_prefix="MYAPP")Env override prefix. DefaultDYNACONF;False= none.Dynaconf(load_dotenv=True)Also read a.envfile from the project root.dynaconf init -f tomlScaffoldconfig.py+settings.toml+.secrets.toml+ gitignore.
settings.NAME★Attribute access — case-insensitive on the first key.settings['NAME']★Item access — identical result.settings.get('NAME', 'fallback')★Safe read with a default if the key is absent.settings.DATABASE.host★Dotted access dives into nested dicts.settings('NAME', 'fallback')Callable form — same as.get().settings.exists('NAME')True/False without raising.
settings.toml★Recommended format. Also.yaml,.json,.ini,.py.settings_files=["a.toml","b.yaml"]Mix formats; later files override earlier keys.settings.local.tomlAny*.local.*loads last — keep it gitignored.includes=["other.yaml"]Loaded after the main files ·preload=loads before.root_path="/etc/app"Base dir for relative file lookups (also checksconfig/).dynaconf write toml -v key=valCLI writes values back into a source file.
export DYNACONF_PORT=80★Overrides any file value. Auto-typed via TOML →int 80.export DYNACONF_DEBUG=trueRealbool, not a string. Floats, lists, dicts too.export DYNACONF_DB__host=h★Double underscore sets a nested key (db.host).export MYAPP_PORT=80Withenvvar_prefix="MYAPP". Must be UPPERCASE.DYNACONF_LIST='@json ["a"]'Dicts/lists from envvars need TOML syntax or@json.Dynaconf(ignore_unknown_envvars=True)Only pull envvars for keys already defined — avoids pollution.
Dynaconf(load_dotenv=True)★Reads.envfrom the project root at startup.DYNACONF_USER=adminSame prefix rules as exported envvars.dotenv_path="/path/.env"Point at a specific dotenv file..env.exampleCommit a template; keep the real.envgitignored.
environments=True★Required for[default]/[development]/[production]sections.[default]Base layer merged under every environment.export ENV_FOR_DYNACONF=production★Switch the active environment at runtime.Dynaconf(env="production")Or select it programmatically at init.settings.from_env('production')A fresh clone reading another env; chain withkeep=True.FORCE_ENV_FOR_DYNACONFnoteBeatsENV_FOR_DYNACONF— use it in pytest fixtures.
@int 42★Force an integer. Also@float,@bool,@str.@bool offParses yes/no/on/off/true/false.@json ["a", 42]★Any heterogeneous list or dict.@none NoneA realNonevalue.port = 8080In files, TOML types are honoured automatically — no token needed.settings.as_int('PORT')Coerce on read:as_bool,as_float,as_json.
@format {this.base_dir}/app.log★{this.KEY}= another setting;{env[VAR]}= an os env var.@jinja {{ this.base_dir }}/xFull Jinja templating (needsjinja2).@format …then| abspathJinja filters likeabspathpost-process the result.@path @format {env[HOME]}/xTokens chain — cast the interpolated string.add_converter("path", Path)Register your own@path-style casting token.
db = {host="h", dynaconf_merge=true}★Merge into the existingdbdict instead of replacing it.[production.database]
dynaconf_merge = {user="x"}Section form of the same merge.tags = "@merge" [c, d]@mergetoken appends to an existing list/dict.DYNACONF_TAGS='@merge [x]'Merge from an envvar too.Dynaconf(merge_enabled=True)Merge everything globally without per-key markers.# default: replacegotchaWithout a merge marker, dicts & lists are overwritten wholesale.
.secrets.toml★Separate file for tokens/passwords — gitignore it.vault_enabled=TruePull secrets from HashiCorp Vault (pip install dynaconf[vault]).redis_enabled=TrueLoad from a Redis server (dynaconf[redis]).dynaconf write vault -s pw=1234CLI writes a secret to the configured store.
from dynaconf import Validator★Declare rules; fail fast on bad config.Validator("PORT", must_exist=True)★Also aliasedrequired=True.Validator("PORT", is_type_of=int)★Type check on load.Dynaconf(validators=[…])Attach at init; orsettings.validators.register(...).settings.validators.validate()★validate_all()collects every error at once.Validator("X", default=5)Supply a default;cast=intcoerces the value.
gte=1, lte=65535Range checks · alsogt,lt,eq,ne.is_in=["a","b"]Membership ·is_not_infor the inverse.len_min=3, len_max=20Length bounds on strings/lists.startswith="http"endswith,contains,conttoo.condition=lambda v: v>0Arbitrary predicate returning bool.when=Validator(...), env="prod"Conditional / env-scoped rule.
settings.as_dict()★Whole config as a plain dict ·to_json().settings.keys() / .values() / .items()Iterate like a mapping.settings.set('KEY', val)Set at runtime ·update({...})for many.settings.get_fresh('KEY')Re-read from source, bypassing the cache.settings.populate_obj(obj)Copy all settings onto another object.with settings.using_env('dev'):Temporarily read another env in a block.
dynaconf -i config.settings list★Dump every resolved key (-i= the instance path).dynaconf -i … get PORTPrint one raw value.dynaconf -i … inspectShow the loading history — which source set each key.dynaconf -i … validateRun validators from the command line.dynaconf init -f yamlScaffold a new project in the chosen format.
from dynaconf import FlaskDynaconf★Extension that backsapp.config.FlaskDynaconf(app)app.config.NAMEnow reads from Dynaconf.export FLASK_ENV=productionSwitches env; prefix isFLASK_, environments on by default.export FLASK_PORT=80Override any config key with theFLASK_prefix.
import dynaconf★
settings = dynaconf.DjangoDynaconf(__name__)Add at the bottom ofsettings.py.from django.conf import settingsUse Django's own settings as usual — now Dynaconf-backed.export DJANGO_DEBUG=falsePrefix isDJANGO_; switch env viaDJANGO_ENV.
from dynaconf import inspect_settings★Full per-key loading history across all sources.inspect_settings(settings, "PORT")Trace one key's value through every layer.from dynaconf import get_historyProgrammatic list of load events.settings.dynaconf.loaded_by_loadersRaw view of what each loader contributed.