Open-source project
dynaconf/dynaconf avatar
dynaconf/dynaconf

dynaconf: layered configuration with environment variables that actually win

Configuration Management for Python ⚙

4,331 stars346 forksPythonMIT

At a glance

What is it?
A Python settings library built around the 12-factor idea, with layered files, typed casting, secrets kept separate and optional Vault and Redis backends, shipping zero required dependencies.
Who is it for?
dynaconf solves a real problem that plain `os.environ` does not: a settings object where a checked-in file holds defaults, a gitignored file holds secrets, and an environment variable overrides both with the value cast to the right type. The zero-dependency base install and the `(MIT AND Apache-2.0)`-style permissiveness of the extras mean it drops into an existing project without arguing about pins.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 38 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What init creates and what each file is for

Installing is one pip command, and initializing is one more. The init output is unusually informative about the design, because it names each file as it is created:

bash
$ cd path/to/your/project/

$ dynaconf init -f toml

⚙️  Configuring your Dynaconf environment
------------------------------------------
🐍 The file `config.py` was generated.

🎛️  settings.toml created to hold your settings.

🔑 .secrets.toml created to hold your secrets.

Three files, three jobs. `config.py` is required and is the only one you import. `settings.toml` is optional and holds ordinary application settings. `.secrets.toml` is optional and holds passwords and tokens, and init adds it to `.gitignore` for you. The format flag accepts `toml|yaml|json|ini|py`, with toml described as the default and the most recommended.

The generated `config.py` is four lines of substance:

python
from dynaconf import Dynaconf

settings = Dynaconf(
    envvar_prefix="DYNACONF",  # export envvars with `export DYNACONF_FOO=bar`.
    settings_files=['settings.yaml', '.secrets.yaml'],  # Load files in the given order.
)

Note that the filenames in the boilerplate are yaml while init defaults to toml, a small inconsistency worth fixing by hand. The README also says plainly that you can skip init and write these yourself, with any filename you like, as long as the module is on your import path.

Reading settings four different ways

The settings object answers to attribute access, uppercase attribute access, dict subscripting and `.get` with a default, which is what makes it drop into code written against anything else:

python
settings.username == "admin"  # dot notation with multi nesting support
settings.PORT == 9900  # case insensitive
settings['password'] == "secret123"  # dict like access
settings.get("nonexisting", "default value")  # Default values just like a dict
settings.databases.name == "mydb"  # Nested key traversing
settings['databases.schema'] == "main"  # Nested key traversing

Case insensitivity is the line to notice. `settings.PORT` and `settings.port` are the same key, which is convenient until you are trying to work out why a value you did not set is being read, and the answer is that casing never mattered.

The file formats are all optional. `pyproject.toml` lists `dependencies = []`, so the base install is genuinely dependency free, with format support and backends behind extras: `yaml` pulls ruamel.yaml, `toml` pulls toml, `ini` pulls configobj, `vault` pulls hvac, and `redis` pulls redis. A project that only needs toml pays for one small package. The `all` extra exists for people who would rather not think about it.

Environment variables override files and get cast

The override mechanism is a prefix plus automatic typing:

bash
# override `port` from settings.toml file and automatically casts as `int` value.
export DYNACONF_PORT=9900

The casting is the part that separates this from reading `os.environ` yourself. A string `9900` from the environment becomes the integer `9900`, so code that does arithmetic or compares against a port number does not need a `int()` call at every site.

The file side of precedence is order-dependent, because `settings_files` is a list and the README says to load files in the given order. Later entries win. That is how `.secrets.toml` overrides a placeholder in `settings.toml` while still letting an environment variable beat both.

Where layering gets more elaborate is environments. The README lists an optional layered system for `default, development, testing, production`, which is how a single codebase carries per-environment files. That feature, the schema validation, the custom loaders and the template substitutions are all one hop away on dynaconf.com rather than described here.

The release notes are a map of where the complexity lives

Three releases are recorded in the last two months, and reading their fix lists tells you more about the library than the feature list does. Version 3.3.3, published 2026-07-22, fixed a regression in lazy evaluation when `dynaboxify=False`. Version 3.3.4 on 2026-07-29 fixed an error when passing a list of values for `env` at init, cleaned up nested `dynaconf_merge` tokens when the parent key is new, kept sibling keys sharing a dotted path leaf name, and fixed a Django early validation integration bug.

Version 3.3.5 on 2026-08-05 continues the same themes: skipping non-string keys when resolving key casing, fixing `get` and `get_fresh` with dotted keys stopping to work after the first call, and fixing `get_history` ignoring its `history_limit` argument.

Read together, those are four separate subsystems: key casing resolution, dotted key paths, merge tokens, and a history mechanism. None of them appear in the README, and the dotted key and casing bugs are exactly the kind that surface in a large codebase using `settings.get('a.b.c')` in a loop. The project is clearly still being hardened, with contributors named per fix and py314 added to the tox list in the same release.

The package metadata is current in the ways that matter: `requires-python = ">=3.10,<3.16"`, classifiers through Python 3.15, Development Status marked Production/Stable, and the CLI declared as `dynaconf = "dynaconf.cli:main"` with the documented subcommands `init, list, write, validate, export`. The last push was 2026-09-01.

Framework extensions, Vault and the shape of the repository

Built-in extensions exist for Django and Flask, and the topic list adds FastAPI and `12factorapp`. The 12-factor reference is the design brief: config from environment variables, not from files baked into the artifact, which is why the envvar override path is the documented first-class route rather than an afterthought. The repository topics also name vault and yaml directly.

The tree shows how much of the effort sits outside the package. Alongside `dynaconf/` and `docs/` there are `tests_functional/`, a `Makefile`, `RELEASING.md`, `SECURITY.md`, `CONTRIBUTORS.md` and `vendor_licenses/`. The Makefile is informative about how the project is developed: it shells out to `uv`, sets `PYTHON_LOWERBOUND := 3.10` with a comment explaining that minification must run on the lowest supported version so the bytecode stays compatible, and runs pytest with `--cov-config pyproject.toml --cov=dynaconf`. It also notes that full integration tests are skipped on Windows and macOS because their docker fixtures are unreliable there.

Secrets handling is the one place the README pushes back on the reader. `dynaconf init` writes `.secrets.*` to `.gitignore`, but the README says keeping it safe locally is your responsibility, and recommends the built-in Hashicorp Vault support for production passwords and tokens.

A CLI for inspecting and exporting what the loader produced

There is a command line entry point declared in `pyproject.toml` as `dynaconf = "dynaconf.cli:main"`, and the README names its subcommands: `init, list, write, validate, export`. Only `init` is demonstrated with output in the README, so the rest is a list rather than a documented interface. That distinction matters for how you use it.

`list` and `export` are the interesting ones in practice, because the fastest way to debug a precedence problem is to see the final value of every key after all layers have been merged. `validate` implies there is a schema to validate against, which matches the feature list entry for settings schema validation. `write` implies persisting the current state back out to a file, which is how you promote a value that was supplied by an environment variable into something checked in.

The README is honest that this is a summary rather than a reference. After the usage walkthrough it lists settings schema validation, custom settings loaders, Vault services and template substitutions under a heading that simply says there is a lot more to do, read the docs at dynaconf.com. Anyone evaluating this against pydantic or Hydra should treat that list as the actual feature surface and the docs as the deciding artefact, because the layering model is where the two libraries differ most.

Editorial conclusion

dynaconf solves a real problem that plain `os.environ` does not: a settings object where a checked-in file holds defaults, a gitignored file holds secrets, and an environment variable overrides both with the value cast to the right type. The zero-dependency base install and the `(MIT AND Apache-2.0)`-style permissiveness of the extras mean it drops into an existing project without arguing about pins. The cost is that precedence has grown subtle enough that three of the five fixes in version 3.3.5 are about it: key casing, dotted keys breaking after a first call, and merge tokens leaking. Start with `dynaconf init -f toml` in a scratch directory, keep your existing settings file, and add the envvar prefix once you know which layer you are fighting.

Frequently asked questions

How do I create a configuration file for a Python project?

With dynaconf, `dynaconf init -f toml` writes `config.py`, `settings.toml` and `.secrets.toml` in the project root and adds the secrets file to `.gitignore`. You can also write those files yourself with any names you like, as long as the module holding the settings object is importable. The settings file is TOML, YAML, JSON, INI or Python, with TOML the recommended default.

How do environment variables override values in dynaconf?

Export a variable named after the setting with the configured prefix, for example `export DYNACONF_PORT=9900` when `envvar_prefix="DYNACONF"`. The value is cast automatically, so that string becomes the integer 9900 rather than staying a string. Environment variables take precedence over values loaded from the files in `settings_files`, which are loaded in the order given.

Is dynaconf compared with pydantic or Hydra a fair one?

They solve different problems, and the comparison is only fair on the loading side. Dynaconf is a layered settings and secrets loader with environment overrides, Vault and Redis backends, and framework extensions for Django and Flask. Recent releases in the 3.3.x line are fixes to key casing, dotted keys, merge tokens and history, which is the area to weigh if you rely on nested lookups. The README points at dynaconf.com for the rest, and at hydroconf for a Rust equivalent.

Official sources

  1. dynaconf/dynaconf on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dynaconf-dynaconf.svg)](https://hysenlabs.com/projects/dynaconf-dynaconf)