Dynaconf
Dynaconf is a tool for managing app configurations. The tool is inspired by the 12-factor application guide, and is focused on assisting with separating your app's configuration from the business logic.
Tip
If you just want to see Dynaconf in action, you can skip to the Example app section.
Providing Configurations to Dynaconf
Dynaconf is very flexible, and can read configurations from a number of formats (.toml
, .json
, .yaml
, .env
), and from the environment itself. The documentation covers different methods of loading environment variables, but the flow I've settled on is defining .toml
settings and secrets files in a config/
directory, breaking the settings into environments ([dev]
, [rc]
, [prod]
) and creating individual Dynaconf()
settings objects for each configuration domain (app
, logging
, database
, etc). This sentence will make more sense as you read on.
Dynaconf reads variables in the following order:
- The environment, or from CLI args
- You can set env variables in your environment (
export VAR_NAME=value
on Linux,$env:VAR_NAME = 'value'
on Windows), but note that you must prepend the variable name withDYNACONF_
for Dynaconf to detect it.- You can sometimes get away with changing the
envvar_prefix=
portion of aDynaconf()
instantiation, but to reliably read a variable from the environment with Dynaconf, you should setDYNACONF_
before the variable name. - For example, if you have an environment variable named
LOG_LEVEL
, you would define it like:export DYNACONF_LOG_LEVEL=...
.
- You can sometimes get away with changing the
- You can also prepend a Python command with variables for Dynaconf to load, like:
LOG_LEVEL='DEBUG' python app.py
- Or, for more durability,
DYNACONF_LOG_LEVEL='DEBUG' python app.py
- You can set env variables in your environment (
.secrets*.toml
,settings*.toml
,.secrets*.json
,settings*.json
, etc- The
*
in each settings file above indicatesdynaconf
will read thesettings.local.toml
/settings.local.json
version of the file, if it exists, before trying to read fromsettings.toml
/settings.json
.
- The
- Default values
- When retrieving a value from a
Dynaconf()
object, you can set a default value, which is 3rd in precedence:DYNACONF_SETTINGS_OBJECT.get("ENV_VAR_NAME", default="The default value")
- When retrieving a value from a
- Global defaults:
- If no value can be determined using a method above,
dynaconf
will try to use global defaults as a fallback, i.e.null
/None
or any value you've configured as a default in your code.
- If no value can be determined using a method above,
Environment Variables
Dynaconf reads from the environment first. You can set environment variables on your host (search online for 'how to set environment variable on environment:
section.
When you define environment variables for Dynaconf to read, you should prepend the variable name with DYNACONF_
. This prefix catches Dynaconf's attention right away and ensure that the value is read. If you are expecting a variable named, for example, LOG_LEVEL
, you would set the environment variable DYNACONF_LOG_LEVEL
, and the value will be accessible in a Dynaconf()
object as LOG_LEVEL
. Note that the DYNACONF_
prefix is not needed when retrieving a value Dynaconf has already loaded, it's only necessary for telling Dynaconf to load that value in the first place.
You can play around with the envvar_prefix=
portion of a Dynaconf()
settings object, but I recommend getting into the habit of using the DYNACONF_
prefix. After much trial and error on my end, this is a surefire way to make sure Dynaconf reads your configuration.
Here are some example environment variables I might set in a Python app I'm writing:
## If I'm using Dynaconf's environments to separate by production, dev, etc
# ENV_FOR_DYNACONF="dev"
## Configure my logging level dynamically
DYNACONF_LOG_LEVEL="DEBUG" # Dynaconf reads this variable as 'LOG_LEVEL'
## Configure a max HTTP timeout on requests
DYNACONF_HTTP_TIMEOUT=15
Setting File (.toml, .json, .yaml, .env)
Dynaconf can read from many file formats, but my preferred format (and the one you will see most often in the documentation and in examples online) is the .toml
format. This guide assumes you are using .toml
files for configuration.
When running in Production, you should use environment variables for configuring your app. These .toml
file examples are great for local development, and you could provide a Production app with a Production settings.toml
file to read, but configuring with environment variables is the best way to load your environment variables into an app.
Warning
This guide uses the term "environment variables" to describe non-sensitive app configurations, like the timezone or logging level. For local development, it is acceptable to use a .secrets.toml
file, but in Production you should always load from a secure vault of some sort. Leaving password in plain text anywhere in the environment is bad practice, and should be avoided even when self-hosting.
Dynaconf will start reading at the current path ./
for .toml
files, and if no settings.toml
/settings.local.toml
file is found, will look for a config/
directory.
I put my .toml
configuration files in a config/
directory because I tend to create separate files for different parts of the app (config/settings.toml
for my app's configuration like logging, config/database/settings.toml
for database settings, etc). This guide will assume your settings.toml
file(s) exist in a config/
directory at the repository root.
There are a number of ways to write a settings.toml
file. This is a simple example of a valid configuration file:
You can also add "environments" to a settings file, and tell Dynaconf to read from a specific environment when it loads the configuration. Environments are created with [brackets]
, and you should provide a [default]
environment where you set all of your variables and a default value:
You can also use environments to define arbitrary environments like [cli]
for running in a pipeline, or [container]
when running in a container environment.
When using production
/dev
environments like this, you must set an ENV_FOR_DYNACONF={dev,prod,testing,etc}
, where the value matches an [environment]
in your settings file. This will tell Dynaconf to only load values from the matching [environment]
.
Secrets file
For local development, you can also store secrets (API keys, passwords, etc) in a .secrets.toml
file. You can commit this file to git, because you should be creating a .secrets.local.toml
file to override the defaults you set in .secrets.toml
. DO NOT COMMIT THE .local VERSION OF YOUR SECRETS FILE TO GIT! You should put real values in .secrets.local.toml
, and you do not want to track those in version control!
Declaring secrets is exactly the same as writing a settings.toml
file. You do not even have to separate these configurations, but it is recommended to separate secrets from configurations for cleanliness and separation of concerns.
Like your settings.toml
file, you should put your "actual" configuration in a non-source controlled .secrets.local.toml
. Dynaconf knows to read the .local.toml
version of a config before the version without .local
in the name, you do not need to do anything special for this functionality:
Reading Configurations with Dynaconf
Look at this code and keep it in mind as you read through the rest of the examples; this is one way to load environment variables with Dynaconf. I prefer this method using .get()
because you can set a default value if no environment variable is detected:
Retrieving env variables with Dynaconf | |
---|---|
We will go into more detail on the code above in another section, but the simple way of describing what is happening above is that your code is reading a Dynaconf()
object (you will see an example of this below) object you named DYNACONF_SETTINGS
for an environment variable named VAR_NAME
, and setting a value of "Default Value"
if the environment variable is not found.
The Dynaconf() object
After defining your configurations in the environment or in a supported file, you need to create a Dynaconf()
object to load those settings. This is where Dynaconf's flexibility really comes into play. You can create a single Dynaconf()
settings object for the entire app, or you can use envvar_prefix=
to "scope" your configurations.
Below are 2 examples of loading environment variables with a Dynaconf()
object. The first example assumes you are using the [environment]
tags to separate configurations by domain, i.e. [logging]
and [database]
, while the second example assumes you're using the [production]
/[dev]
/etc environment definitions.
Environment Tags as Configuration Domains
Example settings.toml
file:
settings.toml | |
---|---|
If you are using Dynaconf environments to separate values into different environments like dev
, prod
, etc, it is advisable to break the .toml
settings files into domain-specific configurations, like settings.logging.toml
/settings.logging.local.toml
, or in separate subdirectories like config/logging/{settings,.secrets}.toml
. You can set the [environment]
Dynaconf should read from using the ENV_FOR_DYNACONF
environment variable; to read the [dev]
configuration, you would set ENV_FOR_DYNACONF="dev"
in your environment. Dynaconf is built to be very flexible, and allows for separating configurations by domain (app, logging, http_server, for example).
You can also store all of your configuration domains in a single settings.toml
file, using variable prefixes like log_
and api_
to separate configuration domains.
Gitignore
You should commit your settings.toml
and .secrets.toml
file(s) to git, but do not put your "real" values in these files. Instead, use default or empty values, and create settings.local.toml
and .secrets.local.toml
files, which should not be committed to git.
Here's an example .gitignore
that will ignore all .local.toml
configurations:
Example .gitignore for Dynaconf | |
---|---|
Example app
Here you can see a simple example of configuring an app with Dynaconf. The app will make a request to the Faker API, with values configured from the environment.
I am using HTTPX as my request client because I prefer it over the requests
package.
For the sake of example, this app will use Dynaconf's environments feature to separate configurations by domain; this example does not use [dev]
/[production]
/etc environments. If you want to use these environments instead, create separate files for each [section]
you see below.
Settings files
First I'll create a config/
directory, and a settings.toml
and .secrets.toml
file. These will be committed to source control, and I will set default values here with the idea that when I clone this repository on a new machine, I will manually create a settings.local.toml
and .secrets.local.toml
file, copying the contents of these 2 source controlled files into the ignored .local
versions and modifying them for local execution.
The FakerAPI does not require an API key, but if it did, I would also declare it in my .secrets.toml
file:
Dynaconf .secrets.toml file | |
---|---|
After creating these 2 files and adding them to source control, I will copy them to the .local
version and edit them:
Copy source controlled settings files to local dev versions | |
---|---|
Again, these .local.toml
versions of the settings files should not be committed to git. You will create these files locally on fresh clones and use them for local testing (i.e. not in a Docker container, where you can just set the values as environment variables).
To tell my app which configuration environment to use (dev, rc, or prod), set an an environment variable called ENV_FOR_DYNACONF
:
Set Dynaconf env on Linux/Mac | |
---|---|
Set Dynaconf env on Windows | |
---|---|
Dynaconf() object
Now I'll create a file named settings.py
, where I will load these configurations. Dynaconf is incredibly flexible in this regard; you can put your Dynaconf()
objects anywhere, they can be alongside your business logic, in a separate file so you can import them throughout the app, you can define multiple Dynaconf()
objects in a single Python project to load different sections of your settings/environment, etc.
I am keeping things simple for this example by creating a settings.py
file, where I will initialize all of my Dynaconf()
objects so I can import them in other scripts.
Python app
Now, in a main.py
, I will configure my logging from the LOGGING_SETTINGS
object, and make a request to the Faker API.