Configuration and secrets

Flask · Practical · 10 min

The same code must run in development, testing and production with different settings and no secrets in git.

app.config is a dict subclass holding Flask's settings (SECRET_KEY, MAX_CONTENT_LENGTH, SESSION_COOKIE_SECURE, ...) and your own. Only uppercase keys are loaded by the helper methods, which is how they skip other module attributes.

Ways to fill it, usually combined in this order so later sources override earlier ones:

  • app.config.from_object("myapp.config.Default"): defaults from a class or module
  • app.config.from_file("config.toml", load=tomllib.load, text=False) or from_pyfile("instance.cfg", silent=True): per-deployment files
  • app.config.from_prefixed_env(): every FLASK_* environment variable, with the prefix removed. Values are parsed as JSON when possible, so FLASK_MAX_CONTENT_LENGTH=1048576 becomes an int and FLASK_DEBUG=true a bool. A double underscore sets nested keys: FLASK_DB__HOST.
  • app.config.update(...) or create_app(test_config) for tests

The instance folder (app.instance_path, next to your package) is meant for machine-local files such as a config file or SQLite database that must not be committed. Create the app with instance_relative_config=True to load files relative to it.

Keep secrets (SECRET_KEY, database passwords, API keys) in environment variables or a secret manager, not in source. Check required settings at startup and fail fast: a missing SECRET_KEY should crash the deploy, not surface as a confusing error on the first login.

Read config in code via current_app.config["KEY"], not by importing the app object.

Example

import os
from flask import Flask

class Default:
    APP_NAME = "Notes"
    MAX_CONTENT_LENGTH = 1024 * 1024
    ITEMS_PER_PAGE = 20
    lowercase_is_ignored = True

os.environ.update({"FLASK_SECRET_KEY": "from-env", "FLASK_ITEMS_PER_PAGE": "50",
                   "FLASK_FEATURES__BETA": "true"})

app = Flask(__name__)
app.config.from_object(Default)
app.config.from_prefixed_env()
if not app.config.get("SECRET_KEY"):
    raise RuntimeError("SECRET_KEY must be set")

print(app.config["ITEMS_PER_PAGE"], type(app.config["ITEMS_PER_PAGE"]).__name__)  # 50 int
print(app.config["FEATURES"])                     # {'BETA': True}
print("lowercase_is_ignored" in app.config)       # False
print(app.config["SECRET_KEY"])                   # from-env

Remember

  • from_object for defaults, from_file/from_pyfile per deployment, from_prefixed_env for FLASK_* vars
  • Only uppercase keys are loaded
  • Secrets come from the environment; fail fast when they are missing

Watch out for

  • Committing SECRET_KEY or database passwords to the repository.

Check yourself

  1. With app.config.from_prefixed_env(), what does FLASK_PAGE_SIZE=25 set?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/config/