Overview
Flask uses a configuration object to store settings that control application behavior. The Config class extends Python’s dictionary and provides multiple ways to load configuration values.
Configuration Basics
Accessing Configuration
from flask import Flask
app = Flask(__name__)
# Access configuration
print(app.config['DEBUG'])
print(app.config.get('SECRET_KEY', 'default-secret'))
# Set configuration
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'your-secret-key'
Built-in Configuration Values
Flask provides many configuration values with defaults:
{
'DEBUG': False,
'TESTING': False,
'SECRET_KEY': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': None,
'TRAP_HTTP_EXCEPTIONS': False,
'PREFERRED_URL_SCHEME': 'http',
'TEMPLATES_AUTO_RELOAD': None,
# ... and many more
}
Loading Configuration
1. From Python Files
# config.py
DEBUG = False
SECRET_KEY = 'your-secret-key'
DATABASE_URI = 'sqlite:///app.db'
# Load configuration
app.config.from_pyfile('config.py')
Only uppercase variables are added to the configuration. This allows you to use lowercase variables for temporary values in the config file.
2. From Objects
class Config:
DEBUG = False
TESTING = False
SECRET_KEY = 'dev-secret-key'
DATABASE_URI = 'sqlite:///app.db'
class DevelopmentConfig(Config):
DEBUG = True
DATABASE_URI = 'sqlite:///dev.db'
class ProductionConfig(Config):
SECRET_KEY = os.environ.get('SECRET_KEY')
DATABASE_URI = os.environ.get('DATABASE_URI')
# Load from object
app.config.from_object(DevelopmentConfig)
# Or from import string
app.config.from_object('myapp.config.ProductionConfig')
3. From Environment Variables
# Load from an environment variable pointing to a file
app.config.from_envvar('MYAPP_SETTINGS')
# Usage:
# export MYAPP_SETTINGS=/path/to/config.py
4. From Prefixed Environment Variables
# Load environment variables starting with FLASK_
app.config.from_prefixed_env()
# Custom prefix
app.config.from_prefixed_env(prefix='MYAPP')
Environment variables are parsed as JSON:
export FLASK_SECRET_KEY='"my-secret-key"'
export FLASK_MAX_CONTENT_LENGTH=16777216
export FLASK_FEATURE_FLAGS='{"enable_api": true}'
# Nested configuration with double underscores
export FLASK_DATABASE__HOST='localhost'
export FLASK_DATABASE__PORT=5432
5. From JSON/TOML Files
import json
import tomllib
# From JSON
app.config.from_file('config.json', load=json.load)
# From TOML
app.config.from_file('config.toml', load=tomllib.load, text=False)
{
"DEBUG": false,
"SECRET_KEY": "your-secret-key",
"DATABASE_URI": "postgresql://localhost/mydb"
}
6. From Mapping
app.config.from_mapping(
DEBUG=True,
SECRET_KEY='dev-secret-key',
DATABASE_URI='sqlite:///dev.db'
)
# Or from a dictionary
config_dict = {'DEBUG': True, 'SECRET_KEY': 'dev-key'}
app.config.from_mapping(config_dict)
Configuration Patterns
Environment-Based Configuration
import os
from flask import Flask
def create_app():
app = Flask(__name__)
# Load default config
app.config.from_object('config.Default')
# Load environment-specific config
env = os.environ.get('FLASK_ENV', 'development')
if env == 'production':
app.config.from_object('config.Production')
elif env == 'testing':
app.config.from_object('config.Testing')
else:
app.config.from_object('config.Development')
# Override from environment variable
app.config.from_envvar('MYAPP_SETTINGS', silent=True)
return app
Instance Folders
Store configuration files outside your package:
app = Flask(__name__, instance_relative_config=True)
# Load from instance folder
app.config.from_pyfile('config.py')
app.config.from_pyfile('production.py', silent=True)
Directory structure:
/myapp
__init__.py
views.py
/instance
config.py
production.py
Configuration Namespace
Extract configuration subsets for components:
app.config['IMAGE_STORE_TYPE'] = 'fs'
app.config['IMAGE_STORE_PATH'] = '/var/app/images'
app.config['IMAGE_STORE_BASE_URL'] = 'http://img.example.com'
# Get namespace
image_config = app.config.get_namespace('IMAGE_STORE_')
# Returns: {
# 'type': 'fs',
# 'path': '/var/app/images',
# 'base_url': 'http://img.example.com'
# }
# Use with external libraries
from some_library import ImageStore
store = ImageStore(**image_config)
Configuration Attributes
Flask provides ConfigAttribute for forwarding configuration to app attributes:
from flask.config import ConfigAttribute
from flask import Flask
class MyFlask(Flask):
debug = ConfigAttribute('DEBUG')
secret_key = ConfigAttribute('SECRET_KEY')
app = MyFlask(__name__)
app.config['DEBUG'] = True
print(app.debug) # True
app.debug = False
print(app.config['DEBUG']) # False
Best Practices
1. Never Commit Secrets
# Good: Load from environment
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key-only-for-development'
DATABASE_PASSWORD = os.environ.get('DATABASE_PASSWORD')
# Bad: Hardcoded secrets
SECRET_KEY = 'my-super-secret-key' # DON'T DO THIS
2. Use Class-Based Configuration
class Config:
"""Base configuration."""
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_TRACK_MODIFICATIONS = False
@property
def DATABASE_URI(self):
return os.environ.get('DATABASE_URI')
class DevelopmentConfig(Config):
"""Development configuration."""
DEBUG = True
TESTING = False
class TestingConfig(Config):
"""Testing configuration."""
DEBUG = True
TESTING = True
DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(Config):
"""Production configuration."""
DEBUG = False
TESTING = False
@property
def DATABASE_URI(self):
uri = os.environ.get('DATABASE_URI')
if not uri:
raise ValueError('DATABASE_URI must be set')
return uri
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
3. Use .env Files for Development
# .flaskenv (committed to version control)
FLASK_APP=myapp
FLASK_ENV=development
# .env (NOT committed - add to .gitignore)
SECRET_KEY=dev-secret-key
DATABASE_URL=postgresql://localhost/myapp_dev
MAIL_USERNAME=[email protected]
MAIL_PASSWORD=your-password
4. Validate Configuration
def validate_config(app):
"""Validate required configuration."""
required = ['SECRET_KEY', 'DATABASE_URI']
for key in required:
if not app.config.get(key):
raise ValueError(f'{key} is required')
if app.config['SECRET_KEY'] == 'dev':
if not app.debug:
raise ValueError('Cannot use dev secret key in production')
app = create_app()
validate_config(app)
5. Document Configuration Options
class Config:
"""Application configuration.
Configuration variables:
SECRET_KEY: Secret key for session signing (required)
DATABASE_URI: Database connection string (required)
MAIL_SERVER: SMTP server for sending emails (default: localhost)
MAIL_PORT: SMTP port (default: 587)
MAX_UPLOAD_SIZE: Maximum file upload size in bytes (default: 16MB)
"""
SECRET_KEY = os.environ.get('SECRET_KEY')
DATABASE_URI = os.environ.get('DATABASE_URI')
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'localhost')
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
MAX_UPLOAD_SIZE = int(os.environ.get('MAX_UPLOAD_SIZE', 16 * 1024 * 1024))
Common Configuration Values
Security
SECRET_KEY = os.environ.get('SECRET_KEY')
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
PERMANENT_SESSION_LIFETIME = timedelta(hours=1)
Database
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
File Uploads
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16 MB
UPLOAD_FOLDER = '/var/www/uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
Email
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'localhost')
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
Testing Configuration
Test your configuration loading:
def test_config():
assert not app.config['TESTING']
def test_testing_config():
app = create_app('testing')
assert app.config['TESTING']
assert app.config['DATABASE_URI'] == 'sqlite:///:memory:'