Configuration Sources in Flask
Flask's app.config object is a dictionary subclass that can be populated from multiple sources: hardcoded defaults via app.config.from_object(SomeClass), environment variables via app.config.from_prefixed_env(), a Python file via from_pyfile(), or a plain dict via from_mapping(). Layering these lets a team keep sensible defaults in code while overriding secrets like SECRET_KEY or DATABASE_URL through environment variables that never get committed to source control.
Cricket analogy: A team sets a default batting order in the pre-season plan but adjusts it on matchday based on pitch conditions and the toss, just as Flask loads default config from a class but can override values from environment variables.
Environment-Based Config Classes
A common pattern defines a base Config class with shared settings, then subclasses for DevelopmentConfig, TestingConfig, and ProductionConfig that override specific values like DEBUG, TESTING, or SQLALCHEMY_DATABASE_URI. Sensitive values like SECRET_KEY and database credentials are read from os.environ inside the base class so they're never hardcoded, and the appropriate subclass is selected at create_app() time based on an environment variable like FLASK_ENV or a deployment-specific entrypoint.
Cricket analogy: A cricket board has a base rulebook but separate addenda for T20, ODI, and Test formats overriding specific rules like over limits, just as DevelopmentConfig, TestingConfig, and ProductionConfig override specific base Config values.
import os
class Config:
SECRET_KEY = os.environ.get("SECRET_KEY", "dev-key-change-me")
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = "sqlite:///dev.db"
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
Secrets and the .env File
Local development commonly uses a .env file loaded via python-dotenv to populate os.environ before the config classes read from it, keeping SECRET_KEY, database passwords, and API keys out of version control. In production, these same variable names are set directly in the deployment environment (Docker Compose env_file, Kubernetes secrets, or a platform's environment variable settings) so the identical config-loading code works unchanged across environments.
Cricket analogy: A team keeps its confidential match strategy in a sealed dressing-room folder that never leaves the venue, just as a .env file with secrets stays out of the public git repository via .gitignore.
A ProductionConfig that raises a KeyError on os.environ['DATABASE_URL'] rather than silently defaulting is intentional: it forces the deployment to explicitly set required secrets rather than accidentally running production against a fallback SQLite file.
Never commit a .env file containing real secrets to version control — add it to .gitignore and commit only a .env.example template with placeholder values, since leaked SECRET_KEY values allow attackers to forge session cookies.
- Flask's app.config supports layered loading: from_object() for code defaults, from_pyfile() for files, from_prefixed_env() for environment variables.
- A base Config class holds shared settings; DevelopmentConfig, TestingConfig, and ProductionConfig override environment-specific values.
- Secrets like SECRET_KEY and DATABASE_URL should be read from os.environ, never hardcoded in source.
- python-dotenv loads a local .env file into os.environ for development convenience.
- .env files with real secrets must be excluded from version control via .gitignore.
- ProductionConfig should fail loudly (KeyError) on missing required secrets rather than silently falling back to insecure defaults.
- The same config-loading code should work unchanged across dev, test, and prod by relying on environment variables.
Practice what you learned
1. What is the purpose of layering config sources (from_object, from_pyfile, environment variables)?
2. Where should SECRET_KEY and database credentials be sourced from in production?
3. Why should a .env file with real secrets never be committed to version control?
4. Why might ProductionConfig intentionally raise a KeyError for a missing DATABASE_URL instead of defaulting to SQLite?
Was this page helpful?
You May Also Like
Flask Application Factories
Why and how to build Flask apps with a create_app() factory function, enabling multiple configured instances, easier testing, and fewer circular imports.
Flask Extensions Overview
A tour of the Flask extension ecosystem, covering how extensions plug into Flask's minimal core, the init_app pattern, and how to evaluate a new extension before adopting it.
Error Handling in Flask
How to use abort(), @app.errorhandler, and logging to turn expected HTTP errors and unexpected exceptions into consistent, safe responses.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics