Configuration & Environments
Reviewed & published by Brayan K
Flask configuration is the set of key-value settings — like SECRET_KEY, DEBUG, and database URLs — that control how your app behaves in development versus production.
Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll load settings from config classes, set them directly from a mapping, pull secrets from environment variables, and split your app cleanly into Dev and Prod environments.
What You'll Learn in This Lesson
- How app.config stores every setting like a dict
- Loading settings from a class with from_object
- Setting keys directly with from_mapping
- Reading env vars with from_prefixed_env
- Building Dev and Prod config classes
- Why SECRET_KEY and DEBUG=False matter
1 app.config and Config Classes
Every Flask app exposes app.config, a dictionary-like object holding all of your settings. You can write to it key-by-key, but the cleanest pattern for real apps is to group settings into a config class and load them all at once with app.config.from_object(...).
A base Config class holds shared defaults, and environment-specific classes like DevConfig and ProdConfig inherit from it and override only what changes. Crucially, from_object copies only UPPERCASE attributes into the config — lowercase names are ignored, which keeps internal helpers out of your settings.
The example below loads DevConfig. Notice that SECRET_KEY is inherited from the base Config class, and .get() safely returns a fallback for keys that were never set.
# app.py — group settings in classes and load with from_object
from flask import Flask
class Config:
DEBUG = False
SECRET_KEY = "change-me"
class DevConfig(Config):
DEBUG = True
class ProdConfig(Config):
DEBUG = False
SECRET_KEY = "real-secret-from-vault"
app = Flask(__name__)
# Load every UPPERCASE attribute from the class into app.config
app.config.from_object(DevConfig)
print(app.config["DEBUG"]) # True
print(app.config["SECRET_KEY"]) # change-me (inherited from Config)
print(app.config.get("MISSING", "fallback")) # fallback
# ✅ Expected output:
# True
# change-me
# fallbackBecause DevConfig overrides only DEBUG, the SECRET_KEY it reports comes straight from the parent Config — class inheritance and config inheritance line up perfectly.
2 Setting Values with from_mapping
When you want to set several keys without defining a class, app.config.from_mapping(...) takes keyword arguments (or a dictionary) and writes them all into the config in one call. It's perfect for small apps, tests, and supplying defaults before layering other sources on top.
Remember that app.config is fundamentally a dict, so you can also assign a single key directly with app.config["KEY"] = value. The example below sets three keys at once, then overwrites one of them.
# app.py — set many keys at once, then tweak one
from flask import Flask
app = Flask(__name__)
# from_mapping sets several keys at once
app.config.from_mapping(
PAGE_SIZE=20,
CACHE_TYPE="simple",
SECRET_KEY="dev-key",
)
print(app.config["PAGE_SIZE"]) # 20
print(app.config["CACHE_TYPE"]) # simple
# app.config behaves like a dict — set a single key directly
app.config["PAGE_SIZE"] = 50
print(app.config["PAGE_SIZE"]) # 50
# ✅ Expected output:
# 20
# simple
# 50from_mapping is ideal for defaults and tests; mixing it with direct app.config["KEY"] = ... assignment gives you fine-grained control whenever you need it.
3 Reading Environment Variables
Secrets and deployment-specific values — API keys, database passwords, the real SECRET_KEY — should never live in your source code. The standard practice is to keep them in environment variables and read them at startup, so the same code runs everywhere while each machine supplies its own secrets.
app.config.from_prefixed_env("FLASK") scans the environment for every variable starting with FLASK_, strips that prefix, and stores the rest as config keys. So FLASK_API_KEY becomes API_KEY. Flask also tries to JSON-parse each value, so the string "25" becomes the integer 25, while plain text stays a string.
# app.py — load FLASK_* environment variables into config
import os
from flask import Flask
# Pretend these were set in the shell before launching the app
os.environ["FLASK_API_KEY"] = "abc123"
os.environ["FLASK_PAGE_SIZE"] = "25"
app = Flask(__name__)
# Pull every FLASK_* env var into config (the prefix is stripped)
app.config.from_prefixed_env("FLASK")
print(app.config["API_KEY"]) # abc123
print(app.config["PAGE_SIZE"]) # 25
# ✅ Expected output:
# abc123
# 25Notice API_KEY stays the string abc123, while PAGE_SIZE is the integer 25 — Flask JSON-parsed the numeric value automatically, so you don't have to convert it yourself.
🔬 Configuration Cheat Sheet
| You want to load config... | Use |
|---|---|
| From a class | app.config.from_object(Cfg) |
| From a dict / keywords | app.config.from_mapping(...) |
| From env vars | app.config.from_prefixed_env("FLASK") |
| Read one value | app.config["KEY"] / .get("KEY", default) |
| The must-have secret | SECRET_KEY |
Rule of thumb: group shared settings in a base Config class, override per environment in DevConfig/ProdConfig, and pull secrets from the environment.
🎯 Your Turn: Fill in the Blanks
Complete the code below. Replace each ___ so it loads DevConfig and reads two settings out of app.config.
# 🎯 YOUR TURN: replace each ___ to load and read config
from flask import Flask
class Config:
DEBUG = False
SECRET_KEY = "change-me"
class DevConfig(Config):
DEBUG = True
app = Flask(__name__)
app.config.___(DevConfig) # load every UPPERCASE attribute from the class
print(app.config[___]) # True -> read the DEBUG setting
print(app.config[___]) # change-me -> read the SECRET_KEY settingHint: the method that copies a class into config is from_object, and config keys are strings like "DEBUG" and "SECRET_KEY".
Common Errors & Quick Reference
❌ Lowercase config names are silently ignored
from_object only copies UPPERCASE attributes. If you write debug = True or secret_key = "x" in your class, Flask skips them entirely and your settings never take effect. Always use DEBUG, SECRET_KEY, and other ALL-CAPS names.
❌ Missing SECRET_KEY breaks sessions and flash
Using session or flash() without a SECRET_KEY raises RuntimeError: The session is unavailable because no secret key was set. Set a real, random key — loaded from the environment in production — before using either feature.
| Task | Code |
|---|---|
| Load a config class | app.config.from_object(DevConfig) |
| Set keys from a dict | app.config.from_mapping(DEBUG=True) |
| Read env vars | app.config.from_prefixed_env("FLASK") |
| Safe read with default | app.config.get("PAGE_SIZE", 10) |
🏆 Mini Challenge: Pick an Environment
Wire up two config classes and choose between them at startup based on an environment variable.
- Define DevConfig (DEBUG on) and ProdConfig (DEBUG off)
- Read an APP_ENV environment variable, defaulting to development
- Load the matching class and print whether DEBUG is on
# 🏆 Mini Challenge: select Dev or Prod from an env var
import os
from flask import Flask
class DevConfig:
DEBUG = True
SECRET_KEY = "dev-only-key"
class ProdConfig:
DEBUG = False
SECRET_KEY = "real-secret-from-vault"
# Pretend the deploy server set this before launching the app
os.environ["APP_ENV"] = "production"
app = Flask(__name__)
env = os.environ.get("APP_ENV", "development")
if env == "production":
app.config.from_object(ProdConfig)
else:
app.config.from_object(DevConfig)
print(f"Environment: {env}") # Environment: production
print(f"Debug mode on? {app.config['DEBUG']}") # Debug mode on? FalseSwitch APP_ENV to anything other than production (or remove it) and the app falls back to DevConfig with DEBUG on — one variable flips your entire environment.
❓ Frequently Asked Questions
Lesson complete — your app is ready for any environment!
You can load settings from config classes with from_object, set keys with from_mapping, pull secrets from the environment with from_prefixed_env, and split your app into Dev and Prod with a single SECRET_KEY and the right DEBUG value.
Practice quiz
What is app.config?
- A read-only constant
- A dictionary-like object storing every setting
- A route decorator
- A logging handler
Answer: A dictionary-like object storing every setting. app.config is a dict-like object holding all of your settings.
What does from_object copy into the config?
- Only UPPERCASE attributes of a class or module
- Every attribute including lowercase
- Only environment variables
- Only methods
Answer: Only UPPERCASE attributes of a class or module. from_object copies only UPPERCASE attributes, ignoring lowercase names.
Which method sets several keys directly from keyword arguments?
- from_object
- from_envvar
- from_mapping
- from_pyfile
Answer: from_mapping. from_mapping(KEY=value, ...) sets several keys at once.
What does from_prefixed_env('FLASK') do?
- Loads a Python file
- Reads a JSON config file
- Copies a config class
- Pulls FLASK_* env vars into config with the prefix stripped
Answer: Pulls FLASK_* env vars into config with the prefix stripped. from_prefixed_env('FLASK') loads FLASK_* vars, stripping the prefix.
What is SECRET_KEY used for?
- Naming the app
- Cryptographically signing session cookies and flash messages
- Enabling DEBUG
- Choosing the database
Answer: Cryptographically signing session cookies and flash messages. SECRET_KEY signs session cookies and flash messages so they can't be tampered with.
Why should DEBUG be False in production?
- It disables routing
- It slows the app down
- True enables an interactive debugger that runs arbitrary code
- True disables logging
Answer: True enables an interactive debugger that runs arbitrary code. DEBUG=True exposes the Werkzeug debugger, a critical security hole in production.
What happens to a lowercase attribute like debug=True in a config class?
- It is silently ignored by from_object
- It overrides DEBUG
- It raises an error
- It becomes 'DEBUG'
Answer: It is silently ignored by from_object. from_object only copies UPPERCASE names, so lowercase ones are ignored.
How do you read a config value with a fallback default?
- app.config.read('KEY')
- app.config.default('KEY')
- app.config.fetch('KEY', d)
- app.config.get('KEY', default)
Answer: app.config.get('KEY', default). app.config.get('KEY', default) safely returns a fallback.
What error does using session without SECRET_KEY raise?
- RuntimeError: the session is unavailable because no secret key was set
- 404 Not Found
- KeyError: SESSION
- ValueError: bad cookie
Answer: RuntimeError: the session is unavailable because no secret key was set. Without SECRET_KEY, session and flash raise a RuntimeError.
Does from_prefixed_env try to JSON-parse values?
- No, everything stays a string
- Only booleans
- Yes, so '25' becomes the integer 25
- Only if you pass parse=True
Answer: Yes, so '25' becomes the integer 25. Flask JSON-parses each value, so '25' becomes int 25 while plain text stays a string.
Continue this course
- Previous: Handling File Uploads
- Next: Application & Request Context (g, current_app) — Understand the app and request contexts, flask.g, and current_app
- Quick reference: Flask cheat sheet
Frequently asked questions
What is app.config in Flask?
app.config is a dictionary-like object that stores every setting your application needs — SECRET_KEY, DEBUG, database URLs, third-party API keys, and your own custom values. Flask and its extensions read from it at runtime, and you read and write it just like a regular Python dict, for example app.config["DEBUG"] or app.config.get("PAGE_SIZE", 10).
What is the difference between from_object, from_mapping, and from_prefixed_env?
from_object(Cfg) copies every UPPERCASE attribute from a class or module into the config, which is ideal for grouping settings into Dev and Prod classes. from_mapping(KEY=value, ...) sets several keys directly from keyword arguments or a dict. from_prefixed_env("FLASK") pulls matching environment variables (like FLASK_API_KEY) into config with the prefix stripped, which keeps secrets out of your source code.
Why does SECRET_KEY matter so much?
Flask uses SECRET_KEY to cryptographically sign session cookies and flash messages so users cannot tamper with them. Without it, sessions and flash() raise a RuntimeError and break. In production it must be a long, random, secret value loaded from an environment variable or a secrets manager — never hard-coded in your repository.
Why should DEBUG be False in production?
DEBUG=True enables the interactive Werkzeug debugger, which executes arbitrary Python code in the browser when an error occurs — a critical security hole that lets attackers run code on your server. It also leaks source code, stack traces, and config values. Keep DEBUG=True only on your machine, and always set it to False (the default) for anything publicly reachable.