Skip to main content

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:'

Build docs developers (and LLMs) love