Overview
WSGI (Web Server Gateway Interface) servers are essential for running Flask applications in production. This guide covers the most popular WSGI servers and their configurations.
Never use Flask’s built-in development server (flask run) in production. It’s not designed to be secure, stable, or efficient.
Gunicorn
Gunicorn (Green Unicorn) is a pure Python WSGI server that’s simple to configure and widely used in production.
Features
Pure Python implementation (easy installation)
Simple configuration
Multiple worker types for performance tuning
Built-in async support with gevent
Integrates easily with hosting platforms
Does not support Windows (but works on WSL)
Installation
Create a virtual environment and install Gunicorn:
cd myapp
python -m venv .venv
source .venv/bin/activate # On Windows WSL: source .venv/bin/activate
pip install . # Install your application
pip install gunicorn
Basic Usage
The syntax for running Gunicorn is {module_import}:{app_variable}:
# If you have: from myapp import app
gunicorn -w 4 'myapp:app'
# If you use the app factory pattern: from myapp import create_app
gunicorn -w 4 'myapp:create_app()'
Output:
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (12345)
Using worker: sync
Booting worker with pid: 12346
Booting worker with pid: 12347
Booting worker with pid: 12348
Booting worker with pid: 12349
Configuration Options
Worker Processes
The -w or --workers option specifies the number of worker processes:
gunicorn -w 4 'myapp:app'
Recommended starting value: (CPU cores * 2) + 1
Binding Address
Don’t bind to 0.0.0.0 when using a reverse proxy, as it would allow bypassing the proxy.
# Local only (default)
gunicorn -w 4 'myapp:app'
# Bind to all interfaces (only without reverse proxy)
gunicorn -w 4 -b 0.0.0.0:8000 'myapp:app'
# Custom host and port
gunicorn -w 4 -b 127.0.0.1:5000 'myapp:app'
Access Logs
# Show access logs on stdout
gunicorn -w 4 --access-logfile=- 'myapp:app'
# Write access logs to file
gunicorn -w 4 --access-logfile=/var/log/gunicorn/access.log 'myapp:app'
Async Workers with gevent
For applications with many concurrent, long-running connections:
pip install gunicorn gevent
gunicorn -k gevent -w 4 'myapp:app'
Requires greenlet>=1.0. When using PyPy, requires PyPy>=7.3.7.
Output:
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (12345)
Using worker: gevent
Booting worker with pid: 12346
Configuration File
Create gunicorn.conf.py for more complex configurations:
# gunicorn.conf.py
import multiprocessing
bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 30
keepalive = 2
# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"
# Security
limit_request_line = 4096
limit_request_fields = 100
limit_request_field_size = 8190
Run with:
gunicorn -c gunicorn.conf.py 'myapp:app'
uWSGI
uWSGI is a fast, compiled server suite with extensive configuration options.
Features
High performance (compiled C program)
Extensive feature set
Multiple protocols support
Built-in caching
Complex configuration
Does not support Windows (but works on WSL)
May require compiler for installation
Installation
Option 1: Pre-compiled wheels (no SSL)
cd myapp
python -m venv .venv
source .venv/bin/activate
pip install . # Install your application
pip install pyuwsgi
Option 2: Compiled with SSL support
pip install uwsgi
# Or force compilation of pyuwsgi
pip install --no-binary pyuwsgi pyuwsgi
Basic Usage
Direct application import:
uwsgi --http 127.0.0.1:8000 --master -p 4 -w myapp:app
Output:
*** Starting uWSGI 2.0.20 (64bit) on [date] ***
*** Operational MODE: preforking ***
mounting myapp:app on /
spawned uWSGI master process (pid: 12345)
spawned uWSGI worker 1 (pid: 12346, cores: 1)
spawned uWSGI worker 2 (pid: 12347, cores: 1)
spawned uWSGI worker 3 (pid: 12348, cores: 1)
spawned uWSGI worker 4 (pid: 12349, cores: 1)
spawned uWSGI http 1 (pid: 12350)
Using app factory pattern:
Create a wsgi.py file:
# wsgi.py
from myapp import create_app
app = create_app()
Then run:
uwsgi --http 127.0.0.1:8000 --master -p 4 -w wsgi:app
Configuration Options
--http - Start HTTP server at specified address
--master - Enable master process
-p or --processes - Number of worker processes
-w - Module and application to load
Binding externally:
uwsgi --http 0.0.0.0:8000 --master -p 4 -w wsgi:app
INI Configuration File
Create uwsgi.ini for persistent configuration:
[uwsgi]
# Application
module = wsgi:app
# Master process
master = true
processes = 4
threads = 2
# Socket
http = 127.0.0.1:8000
# Or use socket for nginx integration
# socket = /tmp/myapp.sock
# chmod-socket = 660
# Performance
max-requests = 1000
vacuum = true
die-on-term = true
# Logging
logto = /var/log/uwsgi/myapp.log
Run with:
Async with gevent
pip install uwsgi gevent
uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app
Output:
*** Starting uWSGI 2.0.20 (64bit) on [date] ***
*** Operational MODE: async ***
spawned uWSGI master process (pid: 12345)
spawned uWSGI worker 1 (pid: 12346, cores: 100)
*** running gevent loop engine ***
Integration with Nginx
uWSGI has optimized integration with Nginx using the uwsgi protocol:
[uwsgi]
module = wsgi:app
master = true
processes = 4
socket = /tmp/myapp.sock
chmod-socket = 660
vacuum = true
Nginx configuration:
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/myapp.sock;
}
Waitress
Waitress is a pure Python WSGI server that’s easy to configure and supports Windows.
Features
Pure Python (easy installation)
Windows support
Easy configuration
Single process with multiple threads
No streaming request support (buffers full request)
Installation
cd myapp
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install . # Install your application
pip install waitress
Basic Usage
The syntax is {module}:{app}:
# Direct application
waitress-serve --host 127.0.0.1 myapp:app
# App factory pattern
waitress-serve --host 127.0.0.1 --call myapp:create_app
Output:
Serving on http://127.0.0.1:8080
Configuration Options
# Custom port
waitress-serve --host 127.0.0.1 --port 5000 myapp:app
# Bind to all interfaces
waitress-serve myapp:app
# Threads
waitress-serve --threads 6 myapp:app
Python Configuration
For more control, use the Python API:
# serve.py
from waitress import serve
from myapp import create_app
app = create_app()
if __name__ == '__main__' :
serve(app, host = '127.0.0.1' , port = 8080 , threads = 6 )
Run with:
mod_wsgi
mod_wsgi integrates directly with Apache httpd server.
Features
Tight Apache integration
Windows support
No separate reverse proxy needed
Requires compiler and Apache development headers
Installation
Installation requires Apache httpd and development headers. On Ubuntu/Debian: sudo apt-get install apache2-dev
Running with mod_wsgi-express
Create wsgi.py with an application variable:
# wsgi.py
from myapp import create_app
application = create_app()
Run the server:
mod_wsgi-express start-server wsgi.py --processes 4
Binding to Privileged Ports
Unlike other WSGI servers, mod_wsgi can run as root and drop privileges:
sudo /home/myuser/.venv/bin/mod_wsgi-express start-server \
/home/myuser/wsgi.py \
--user myuser --group myuser --port 80 --processes 4
Comparison Table
Feature Gunicorn uWSGI Waitress mod_wsgi Platform Unix/Linux Unix/Linux Cross-platform Cross-platform Installation Easy Moderate Easy Complex Performance Good Excellent Good Good Configuration Simple Complex Simple Moderate Worker Types Sync, Async Sync, Async, Threaded Threaded Sync, Threaded Best For General production High-performance Windows, simple apps Apache infrastructure
Best Practices
Choose the Right Worker Count
Start with (CPU cores * 2) + 1 workers and adjust based on your application’s characteristics:
CPU-bound : Use fewer workers (1-2 per core)
I/O-bound : Use more workers or async workers
Mixed : Start with the formula above
Monitor memory usage as each worker consumes memory.
Set appropriate timeouts to prevent workers from hanging: # Gunicorn
gunicorn --timeout 30 'myapp:app'
# uWSGI
uwsgi --http-timeout 30 -w wsgi:app
Enable access logs for monitoring and debugging: # Gunicorn
gunicorn --access-logfile=/var/log/gunicorn/access.log 'myapp:app'
# uWSGI
uwsgi --logto=/var/log/uwsgi/myapp.log -w wsgi:app
Use graceful reloads to deploy updates without downtime: # Gunicorn
kill -HUP < master_pi d >
# uWSGI
uwsgi --reload /tmp/myapp.pid
Next Steps
Production Configuration Learn production best practices, security, and monitoring
Deployment Overview Return to the deployment overview