Flask Application Factories and Production Setup
Flask's minimal "hello world" — a module-level app = Flask(__name__) — is perfect for learning and painful for production. Module-level apps make configuration depend on import order, make it hard to create separate apps for tests, and invite global state that breaks under multiple workers. The application factory pattern fixes this: a create_app() function builds and configures a fresh app, initializes extensions, and registers blueprints, so every environment and test gets an app configured exactly as intended.
Production Flask also needs decisions the framework leaves to you: where configuration and secrets come from, how per-request state is stored and cleaned up, which WSGI server and worker model run the app, how it sits behind a reverse proxy, and how migrations and health checks fit into deployment. Documenting these choices is as important as the code.
TL;DR
- Build apps in a
create_app()factory; create extensions once and bind them withinit_app(app). - Layer configuration: defaults → environment class → instance config → environment variables; fail fast on missing secrets.
- Organize routes in blueprints; keep per-request state in
gand clean up in teardown handlers, not in globals. - Test with a fresh app per test or session using a testing configuration.
- Run under Gunicorn (or another production WSGI server) with a documented worker type and count, behind Nginx or a load balancer.
- Configure ProxyFix for exactly the proxies you trust, log to stdout, expose a health check, and run migrations as a separate deploy step.
Quick Example
An application factory (app/__init__.py):
Configuration classes and a test that builds its own app:
Running it in production, with the worker model stated explicitly:
Core Concepts
The Application Factory
A factory returns a configured app. It enables multiple apps with different settings (tests, CLI tools, workers), avoids circular imports by importing blueprints inside the function, and makes configuration explicit. The Flask CLI and WSGI servers can call it directly: flask --app "app:create_app()" run --debug for development, gunicorn "app:create_app()" for production.
Layered Configuration
Never ship with DEBUG = True or a default SECRET_KEY in production. See Twelve-Factor App and Configuration and Environment Management.
Extensions and Blueprints
Extensions such as Flask-SQLAlchemy, Flask-Migrate, and Flask-Login follow the init_app pattern: create the object at module level without an app, then bind it inside the factory. Blueprints group routes, templates, and error handlers by feature and are registered with URL prefixes. See SQLAlchemy.
Contexts and Request State
Flask pushes an application context (current_app, g) and a request context (request, session) for each request. Store per-request objects — the current user, a database session, a request ID — on g, and release resources in teardown_request or teardown_appcontext handlers, which run even when the request fails. Module-level mutable state is shared across requests within a worker and is not shared across workers, so it is wrong in both directions.
Workers and Deployment
Put Nginx or a load balancer in front for TLS, buffering slow clients, and static files, and tell Flask how many proxies to trust with ProxyFix so request.remote_addr and URL schemes are correct. See Nginx.
Operations
Log to stdout in a structured format with request IDs; expose /healthz (process alive) and optionally a readiness check (dependencies reachable); run database migrations as a deploy step before switching traffic, never on import; and serve static assets from Nginx or a CDN.
Best Practices
Fail Fast on Configuration
Validate required settings and secrets in the factory so misconfigured deployments never start.
Keep the Factory Side-Effect Free
Do not connect to databases, run migrations, or start threads at import or factory time beyond what is needed to register components.
Document the Worker Model
Record worker class, count, timeouts, and thread-safety assumptions alongside the deployment configuration.
Trust Proxies Precisely
Configure ProxyFix with the exact number of proxies; trusting more lets clients spoof IP addresses and schemes.
Common Mistakes
Module-Level App and Config
Global Mutable State
Development Server in Production
FAQ
What is a Flask application factory?
A function, usually create_app(), that creates, configures, and returns a new Flask app — initializing extensions and registering blueprints — instead of defining a global app at import time.
How should Flask load configuration?
From layered sources: default config classes, an instance folder for host-specific settings, and environment variables for secrets, validated at startup.
Why use init_app with extensions?
It separates creating the extension object from binding it to an app, so one extension instance works with multiple apps created by the factory, including test apps.
How many Gunicorn workers should I run?
Start with about 2 × CPU cores + 1 for sync workers, then adjust based on memory per worker, latency, and whether requests are CPU- or I/O-bound.
Why do I need ProxyFix?
Behind a reverse proxy, Flask sees the proxy's address and scheme. ProxyFix reads forwarded headers from a configured number of trusted proxies so client IPs and HTTPS detection are correct.
Related Topics
- Flask — The Flask framework
- Python — The language Flask is built on
- SQLAlchemy — Database access in Flask apps
- Nginx — Reverse proxy in front of Gunicorn
- The Twelve-Factor App — Configuration and process principles
- Configuration and Environment Management — Managing settings across environments