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

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
# fallback

Because 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
# 50

from_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
# 25

Notice 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 classapp.config.from_object(Cfg)
From a dict / keywordsapp.config.from_mapping(...)
From env varsapp.config.from_prefixed_env("FLASK")
Read one valueapp.config["KEY"] / .get("KEY", default)
The must-have secretSECRET_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 setting

Hint: 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.

TaskCode
Load a config classapp.config.from_object(DevConfig)
Set keys from a dictapp.config.from_mapping(DEBUG=True)
Read env varsapp.config.from_prefixed_env("FLASK")
Safe read with defaultapp.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.

# 🏆 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? False

Switch 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

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.