Skip to content

Dynaconf Quick Start

Dynaconf

Configuration Management for Python.

MIT License PyPIcodecov GitHub Release Date GitHub last commit Discussions

Features

  • Inspired by the 12-factor application guide
  • Settings management (default values, validation, parsing, templating)
  • Protection of sensitive information (passwords/tokens)
  • Multiple file formats toml|yaml|json|ini|py and also customizable loaders.
  • Full support for environment variables to override existing settings (dotenv support included).
  • Optional layered system for multi environments [default, development, testing, production] (also called multi profiles)
  • Built-in support for Hashicorp Vault and Redis as settings and secrets storage.
  • Built-in extensions for Django and Flask web frameworks.
  • CLI for common operations such as init, list, write, validate, export.

Installation

Install from pypi

pip install dynaconf

Initialize Dynaconf on your project

Using Python Only

Using Python only

In your project's root directory run dynaconf init command.

cd path/to/your/project/
dynaconf init -f toml
The command output must be:

⚙ī¸  Configuring your Dynaconf environment
------------------------------------------
🐍 The file `config.py` was generated.

🎛ī¸  settings.toml created to hold your settings.

🔑 .secrets.toml created to hold your secrets.

🙈 the .secrets.* is also included in `.gitignore`
beware to not push your secrets to a public repo.

🎉 Dynaconf is configured! read more on https://dynaconf.com

ℹī¸ You can choose toml|yaml|json|ini|py on dynaconf init -f <fileformat>, toml is the default and also the most recommended format for configuration.

Dynaconf init command creates the following files

.
├── config.py       # Where you import your settings object (required)
├── .secrets.toml   # Sensitive data like passwords and tokens (optional)
└── settings.toml   # Application settings (optional)

On your own code you import and use settings object imported from your config.py file

from config import settings

assert settings.key == "value"
assert settings.number == 789
assert settings.a_dict.nested.other_level == "nested value"
assert settings['a_boolean'] is False
assert settings.get("DONTEXIST", default=1) == 1

In this file a new instance of Dynaconf settings object is initialized and configured.

from dynaconf import Dynaconf

settings = Dynaconf(
    settings_files=['settings.toml', '.secrets.toml'],
)
More options are described on Dynaconf Configuration

Optionally store settings in a file (or in multiple files)

key = "value"
a_boolean = false
number = 1234
a_float = 56.8
a_list = [1, 2, 3, 4]
a_dict = {hello="world"}

[a_dict.nested]
other_level = "nested value"
More details in Settings Files

Optionally store sensitive data in a local-only file .secrets.toml

password = "s3cr3t"
token = "dfgrfg5d4g56ds4gsdf5g74984we5345-"
message = "This file doesn't go to your pub repo"

⚠ī¸ dynaconf init command puts the .secrets.* in your .gitignore to avoid it being exposed on public repos but it is your responsibility to keep it safe in your local environment, also the recommendation for production environments is to use the built-in support for Hashicorp Vault service for password and tokens.

# Secrets don't go to public repos
.secrets.*

read more on Secrets

Optionally override using prefixed environment variables. (.env files are also supported)

export DYNACONF_NUMBER=789
export DYNACONF_FOO=false
export DYNACONF_DATA__CAN__BE__NESTED=value
export DYNACONF_FORMATTED_KEY="@format {this.FOO}/BAR"
export DYNACONF_TEMPLATED_KEY="@jinja {{ env['HOME'] | abspath }}"

ℹī¸ You can create the files yourself instead of using the dynaconf init command and it gives any name you want instead of the default config.py (the file must be in your importable python path)

Or using Flask

Using Flask

In your Flask project import FlaskDynaconf extension and initialize it as a Flask extension.

from flask import Flask
from dynaconf import FlaskDynaconf

app = Flask(__name__)

FlaskDynaconf(app, settings_files=["settings.toml"])

Across your Flask application you can access all the settings variables direct from app.config that is now replaced by a dynaconf settings instance.

@app.route("/a_view)
def a_view():
    app.config.NAME == "BRUNO"
    app.config['DEBUG'] is True
    app.config.SQLALCHEMY_DB_URI == "sqlite://data.db"

Optionally store settings in a file using layered environments.

[default]
key = "value"
a_boolean = false
number = 1234

[development]
key = "development value"
SQLALCHEMY_DB_URI = "sqlite://data.db"

[production]
key = "production value"
SQLALCHEMY_DB_URI = "postgresql://..."

ℹī¸ On Flask, settings files are layered in multiple environments by default, you can disable it by passing environments=False to FlaskDynaconf extension.

More details in Settings Files

Optionally store sensitive data in a local-only file .secrets.toml

[development]
password = "s3cr3t"
token = "dfgrfg5d4g56ds4gsdf5g74984we5345-"
message = "This file doesn't go to your pub repo"

⚠ī¸ put .secrets.* in your .gitignore to avoid it being exposed on public repos but it is your responsibility to keep it safe in your local environment, also the recommendation for production environments is to use the built-in support for Hashicorp Vault service for password and tokens.

# Secrets don't go to public repos
.secrets.*

read more on Secrets

Optionally override any setting using FLASK_ prefixed environment variables. (.env files are also supported)

export FLASK_ENV=production
export FLASK_NUMBER=789
export FLASK_FOO=false

Dynaconf can also load your Flask extensions for you see more details in Flask Extension

Or using Django

Using Django

Ensure you have DJANGO_SETTINGS_MODULE exported:

export DJANGO_SETTINGS_MODULE=yourproject.settings

On the same folder where your manage.py is located run dynaconf init command.

dynaconf init -f yaml

Then follow the instructions on the terminal.

Django app detected
⚙ī¸  Configuring your Dynaconf environment
------------------------------------------
🎛ī¸  settings.yaml created to hold your settings.

🔑 .secrets.yaml created to hold your secrets.

🙈 the .secrets.yaml is also included in `.gitignore`
beware of not pushing your secrets to a public repo
or use dynaconf builtin support for Vault Servers.

⁉  path/to/yourproject/settings.py is found do you want to add dynaconf? [y/N]:

Answer y

🎠  Now your Django settings are managed by Dynaconf
🎉  Dynaconf is configured! read more at https://dynaconf.com

ℹī¸ On Django the recommended file format is yaml because it can hold complex data structures easier, however, you can choose to use toml, json, ini or even keep your settings in .py format.

On your Django views, models and all other places you can now use django.conf.settings normally because it is replaced by a Dynaconf settings object.

from django.conf import settings


def index(request):
    assert settings.DEBUG is True
    assert settings.NAME == "Bruno"
    assert settings.DATABASES.default.name == "db"
    assert settings.get("NONEXISTENT", 2) == 2

Optionally store settings in a file using layered environments. this file must be located in the folder where your manage.py is located.

default:
    ALLOWED_HOSTS:
        - '*'
    INSTALLED_APPS:
        - django.contrib.admin
        - django.contrib.auth
        - django.contrib.contenttypes
        - django.contrib.sessions
        - django.contrib.messages
        - django.contrib.staticfiles
production:
    ALLOWED_HOSTS:
        - 'server.prod.com'

ℹī¸ On Django settings files are layered in multiple environments by default, you can disable it by passing environments=False to FlaskDynaconf extension.

More details in Settings Files

Optionally store sensitive data in a local-only file .secrets.toml

development:
    SECRET_KEY: 43grng9398534nfkjer
production:
    SECRET_KEY: vfkjndkjg098gdf90gudfsg

⚠ī¸ put .secrets.* in your .gitignore to avoid it being exposed on public repos but it is your responsibility to keep it safe in your local environment, also the recommendation for production environments is to use the built-in support for Hashicorp Vault service for password and tokens.

# Secrets don't go to public repos
.secrets.*

read more on Secrets

Optionally override any setting using DJANGO_ prefixed environment variables. (.env files are also supported)

export DJANGO_ENV=production
export DJANGO_NUMBER=789
export DJANGO_FOO=false
export DJANGO_ALLOWED_HOSTS="['*']"
export DJANGO_DEBUG=false
export DJANGO_DATABASES__default__NAME=othername

Once initialized dynaconf includes the following on the bottom of your existing settings.py and you need to keep these lines there.

# HERE STARTS DYNACONF EXTENSION LOAD
import dynaconf  # noqa
settings = dynaconf.DjangoDynaconf(__name__)  # noqa
# HERE ENDS DYNACONF EXTENSION LOAD (No more code below this line)

More details in Django Extension


Tip

The dynaconf CLI has more useful commands such as list | export, init, write and validate read more on CLI

Defining your settings variables

Dynaconf prioritizes the use of environment variables and you can optionally store settings in Settings Files using any of toml|yaml|json|ini|py extension.

On env vars

environment variables are loaded by Dynaconf if prefixed either with DYNACONF_ or a CUSTOM_ name that you can customize on your settings instance, or FLASK_ and DJANGO_ respectively if you are using extensions.

export DYNACONF_FOO=BAR                      # string value
                                             # default DYNACONF_ prefix

export DYNACONF_NUMBER=123                   # automatically loaded as int

export DJANGO_ALLOWED_HOSTS="['*', 'other']" # DJANGO_ extension prefix
                                             # automatically loaded as a list

export FLASK_DEBUG=true                      # FLASK_ extension prefix
                                             # automatically loaded as boolean

export CUSTOM_NAME=Bruno                     # CUSTOM_ prefix as specified in
                                             # Dynaconf(envvar_prefix="custom")

export DYNACONF_NESTED__LEVEL__KEY=1         # Double underlines
                                             # denotes nested settings
                                             # nested = {
                                             #     "level": {"key": 1}
                                             # }

More details on environment variables.

On files

Optionally you can store settings in files, dynaconf supports multiple file formats, you are recommended to choose one format but you can also use mixed settings formats across your application.

Supported formats

Create your settings in the desired format and specify it on settings_files argument on your dynaconf instance or pass it in -f <format> if using dynaconf init command.

The following are the currently supported formats:

  • .toml - Default and recommended file format.
  • .yaml|.yml - Recommended for Django applications.
  • .json - Useful to reuse existing or exported settings.
  • .ini - Useful to reuse legacy settings.
  • .py - Not Recommended but supported for backwards compatibility.
  • .env - Useful to automate the loading of environment variables.

Tip

Can't find the file format you need for your settings? You can create your custom loader and read any data source. read more on extending dynaconf

Key types

Dynaconf will try to preserve non-string integers such as 1: foo in yaml, or arbitrary types defined within python, like settings.set("a", {1: "b", (1,2): "c"}).

This is intended for special cases only, as envvars and most file loaders won't support non-string key types.

Reading settings from files

On files by default dynaconf loads all the existing keys and sections as first-level settings.

name = "Bruno"
name: Bruno
{"name": "Bruno"}
name = 'Bruno'
NAME = "Bruno"

⚠ī¸ on .py files dynaconf only read UPPERCASE variables.

Then on your application code:

settings.name == "Bruno"

Layered environments on files

It is also possible to make dynaconf read the files separated by layered environments so each section or first-level key is loaded as a distinct environment.

Warning

To enable layered environments the argument environments must be set to True, otherwise, dynaconf will ignore layers and read all first-level keys as normal values.

settings = Dynaconf(environments=True)
[default]
name = ""
[development]
name = "developer"
[production]
name = "admin"
default:
    name: ''
development:
    name: developer
production:
    name: admin
{
    "default": {
        "name": ""
    },
    "development": {
        "name": "developer"
    },
    "production": {
        "name": "admin"
    }
}
[default]
name = ""
[development]
name = "developer"
[production]
name = "admin"

ℹī¸ You can define a custom environment using the name you want [default] and [global] are the only environments that are special. You can for example name it [testing] or [anything]

Then in your program you can use environment variables to switch environments.

export ENV_FOR_DYNACONF=development

settings.name == "developer"

export ENV_FOR_DYNACONF=production

settings.name == "admin"

Warning

On Flask and Django extensions the default behaviour is already the layered environments. Also to switch the environment you use export FLASK_ENV=production or export DJANGO_ENV=production respectively.

Tip

It is also possible to switch environments programmatically passing env="development" to Dynaconf class on instantiation.

Read more on Settings Files

Reading settings variables

An instance of Dynaconf settings is a dict like object that provides multiple ways to access variables.

from path.to.project.config import settings
# or
from django.conf import settings
# or
settings = app.config  # flask

# reading settings variables

settings.username == "admin"                  # dot notation
settings.PORT == settings.port == 9900        # case insensitive
isinstance(settings.port, int)                # automatic type casting
settings.databases.name == "mydb"             # Nested keys traversing
settings['password'] == "secret123"           # dict like item access
settings['databases.schema'] == "main"        # Nested items traversing
settings.get("nonexisting", "default value")  # Default values just like a dict
settings("number", cast="@int")                  # customizable forcing of casting

for key, value in settings.items():           # dict like iteration
    print(key, value)

Spaces in keys

If the key has spaces it can be accessed by replacing the space with an underscore.

ROOT:
    MY KEY: "value"
settings.root.my_key == "value"
settings.root["my key"] == "value"

Validating your settings

Dynaconf offers the Validator object for you to define rules for your settings schema, this works in a declarative way and you can validate your settings in 2 ways.

Writing Validators as Python objects

On your config.py

from dynaconf import Dynaconf, Validator

settings = Dynaconf(
    validators=[
        Validator("name", eq="Bruno") & Validator("username", ne="admin"),
        Validator("port", gte=5000, lte=8000),
        Validator("host", must_exist=True) | Validator("bind", must_exist=True)
    ]
)

Once passed in on validators argument, Dynaconf will evaluate each of the rules before your settings are first read.

An alternative way is registering validators using.

settings.validators.register(Validator("field", **rules))

You can also force the earlier validation by making a call to validate after your settings instantiation.

settings.validators.validate()

In case of errors it will raise ValidationError and exit with status 1 (useful for CI)

Writing Validators as a toml file

You can also alternatively place a dynaconf_validators.toml file in the root of your project.

[development]
name = {must_exist=true}
port = {gte=5000, lte=9000}

Following the same rules used in the Validator class, then you can trigger the validation via the command line using.

dynaconf -i config.settings validate

Read more on Validation

Dependencies

Vendored

Dynaconf core has no dependency. It uses vendored copies of external libraries such as python-box, click, python-dotenv, ruamel-yaml, python-toml

Optional dependencies

To use external services such as Redis and Hashicorp Vault it is necessary to install additional dependencies using pip [extra] argument.

Vault

pip install dynaconf[vault]

Redis

pip install dynaconf[redis]

Read more on external loaders

License

This project is licensed under the terms of the MIT license.

More

If you are looking for something similar to Dynaconf to use in your Rust projects: https://github.com/rubik/hydroconf