Last updated on 2020-02-26

Configuration

Configuration

When you’re learning Flask, configuration seems simple. You just define some variables in config.py and everything works. That simplicity starts to fade away when you have to manage configuration for a production application. You may need to protect secret API keys or use different configurations for different environments (e.g. development and production environments). In this chapter we’ll go over some advanced Flask features that makes this managing configuration easier.

The simple case

A simple application may not need any of these complicated features. You may just need to put config.py in the root of your repository and load it in app.py or yourapp/__init__.py

The config.py file should contain one variable assignment per line. When your app is initialized, the variables in config.py are used to configure Flask and its extensions are accessible via the app.config dictionary – e.g. app.config["DEBUG"].

1
2
3
DEBUG = True # Turns on debugging features in Flask
BCRYPT_LOG_ROUNDS = 12 # Configuration for the Flask-Bcrypt extension
MAIL_FROM_EMAIL = "robert@example.com" # For use in application emails

Configuration variables can be used by Flask, extensions or you. In this example, we could use app.config["MAIL_FROM_EMAIL"] whenever we needed the default “from” address for a transactional email – e.g. password resets. Putting that information in a configuration variable makes it easy to change it in the future.

1
2
3
4
5
6
7
# app.py or app/__init__.py
from flask import Flask

app = Flask(__name__)
app.config.from_object('config')

# Now we can access the configuration variables via app.config["VAR_NAME"].

Variable

Decription

Recommendation

DEBUG

Gives you some handy tools for debugging errors. This includes a web-based stack trace and interactive Python console for errors.

Should be set to True in development and False in production.

SECRET_KEY

This is a secret key that is used by Flask to sign cookies. It’s also used by extensions like Flask-Bcrypt. You should define this in your instance folder to keep it out of version control. You can read more about instance folders in the next section.

This should be a complex random value.

BCRYPT_LOG_ROUNDS

If you’re using Flask-Bcrypt to hash user passwords, you’ll need to specify the number of “rounds” that the algorithm executes in hashing a password. If you aren’t using Flask-Bcrypt, you should probably start. The more rounds used to hash a password, the longer it’ll take for an attacker to guess a password given the hash. The number of rounds should increase over time as computing power increases.

Later in this book we’ll cover some of the best practices for using Bcrypt in your Flask application.

Warning

Make sure DEBUG is set to False in production. Leaving it on will allow users to run arbitrary Python code on your server.

Instance folder

Sometimes you’ll need to define configuration variables that contain sensitive information. We’ll want to separate these variables from those in config.py and keep them out of the repository. You may be hiding secrets like database passwords and API keys, or defining variables specific to a given machine. To make this easy, Flask gives us a feature called instance folders. The instance folder is a sub-directory of the repository root and contains a configuration file specifically for this instance of the application. We don’t want to commit it into version control.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
config.py
requirements.txt
run.py
instance/
  config.py
yourapp/
  __init__.py
  models.py
  views.py
  templates/
  static/

Using instance folders

To load configuration variables from an instance folder, we use app.config.from_pyfile(). If we set instance_relative_config=True when we create our app with the Flask() call, app.config.from_pyfile() will load the specified file from the instance/ directory.

1
2
3
4
5
# app.py or app/__init__.py

app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
app.config.from_pyfile('config.py')

Now, we can define variables in instance/config.py just like you did in config.py. You should also add the instance folder to your version control system’s ignore list. To do this with Git, you would add instance/ on a new line in .gitignore.

Secret keys

The private nature of the instance folder makes it a great candidate for defining keys that you don’t want exposed in version control. These may include your app’s secret key or third-party API keys. This is especially important if your application is open source, or might be at some point in the future. We usually want other users and contributors to use their own keys.

1
2
3
4
5
6
# instance/config.py

SECRET_KEY = 'Sm9obiBTY2hyb20ga2lja3MgYXNz'
STRIPE_API_KEY = 'SmFjb2IgS2FwbGFuLU1vc3MgaXMgYSBoZXJv'
SQLALCHEMY_DATABASE_URI= \
"postgresql://user:TWljaGHFgiBCYXJ0b3N6a2lld2ljeiEh@localhost/databasename"

Minor environment-based configuration

If the difference between your production and development environments are pretty minor, you may want to use your instance folder to handle the configuration changes. Variables defined in the instance/config.py file can override the value in config.py. You just need to make the call to app.config.from_pyfile() after app.config.from_object(). One way to take advantage of this is to change the way your app is configured on different machines.

1
2
3
4
5
6
7
8
9
# config.py

DEBUG = False
SQLALCHEMY_ECHO = False


# instance/config.py
DEBUG = True
SQLALCHEMY_ECHO = True

In production, we would leave the variables in the above listing out of instance/config.py and it would fall back to the values defined in config.py.

Note

Configuring based on environment variables

The instance folder shouldn’t be in version control. This means that you won’t be able to track changes to your instance configurations. That might not be a problem with one or two variables, but if you have finely tuned configurations for various environments (production, staging, development, etc.) you don’t want to risk losing that.

Flask gives us the ability to choose a configuration file on load based on the value of an environment variable. This means that we can have several configuration files in our repository and always load the right one. Once we have several configuration files, we can move them to their own config directory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
requirements.txt
run.py
config/
  __init__.py # Empty, just here to tell Python that it's a package.
  default.py
  production.py
  development.py
  staging.py
instance/
  config.py
yourapp/
  __init__.py
  models.py
  views.py
  static/
  templates/

In this listing we have a few different configuration files.

config/default.py

Default values, to be used for all environments or overridden by individual environments. An example might be setting DEBUG = False in config/default.py and DEBUG = True in config/development.py.

config/development.py

Values to be used during development. Here you might specify the URI of a database sitting on localhost.

config/production.py

Values to be used in production. Here you might specify the URI for your database server, as opposed to the localhost database URI used for development.

config/staging.py

Depending on your deployment process, you may have a staging step where you test changes to your application on a server that simulates a production environment. You’ll probably use a different database, and you may want to alter other configuration values for staging applications.

To decide which configuration file to load, we’ll call app.config.from_envvar().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# yourapp/__init__.py

app = Flask(__name__, instance_relative_config=True)

# Load the default configuration
app.config.from_object('config.default')

# Load the configuration from the instance folder
app.config.from_pyfile('config.py')

# Load the file specified by the APP_CONFIG_FILE environment variable
# Variables defined here will override those in the default configuration
app.config.from_envvar('APP_CONFIG_FILE')

The value of the environment variable should be the absolute path to a configuration file.

How we set this environment variable depends on the platform in which we’re running the app. If we’re running on a regular Linux server, we can set up a shell script that sets our environment variables and runs run.py.

1
2
3
4
# start.sh

export APP_CONFIG_FILE=/var/www/yourapp/config/production.py
python run.py

start.sh is unique to each environment, so it should be left out of version control. On Heroku, we’ll want to set the environment variables with the Heroku tools. The same idea applies to other PaaS platforms.

Summary

  • A simple app may only need one configuration file: config.py.

  • Instance folders can help us hide secret configuration values.

  • Instance folders can be used to alter an application’s configuration for a specific environment.

  • We should use environment variables and app.config.from_envvar() for more complicated environment-based configurations.