Skip to main content

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:
uwsgi --ini uwsgi.ini

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:
python serve.py

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

pip install mod_wsgi
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

FeatureGunicornuWSGIWaitressmod_wsgi
PlatformUnix/LinuxUnix/LinuxCross-platformCross-platform
InstallationEasyModerateEasyComplex
PerformanceGoodExcellentGoodGood
ConfigurationSimpleComplexSimpleModerate
Worker TypesSync, AsyncSync, Async, ThreadedThreadedSync, Threaded
Best ForGeneral productionHigh-performanceWindows, simple appsApache infrastructure

Best Practices

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_pid>

# uWSGI
uwsgi --reload /tmp/myapp.pid

Next Steps

Production Configuration

Learn production best practices, security, and monitoring

Deployment Overview

Return to the deployment overview

Build docs developers (and LLMs) love