Flask Gunicorn Setup
Introduction
When you're developing Flask applications, you typically use Flask's built-in development server. However, this server isn't designed for production environments – it's single-threaded, can't handle concurrent requests efficiently, and lacks security features necessary for real-world deployments.
This is where Gunicorn (Green Unicorn) comes in. Gunicorn is a Python WSGI (Web Server Gateway Interface) HTTP server that acts as a middle layer between your Flask application and the internet. By using Gunicorn with Flask, you create a robust, production-ready architecture capable of handling multiple concurrent requests with better performance and stability.
In this tutorial, we'll learn how to configure Gunicorn to serve your Flask application in a production environment.
Prerequisites
Before we begin, make sure you have:
- A working Flask application
- Python 3.6+ installed
- Basic understanding of command line operations
- Understanding of virtual environments in Python
What is Gunicorn?
Gunicorn (Green Unicorn) is a Python WSGI HTTP server for UNIX systems. It:
- Implements the WSGI specification to interface with Python web applications
- Provides a pre-fork worker model (spawning multiple processes to handle requests)
- Supports various worker configurations for different workloads
- Is lightweight, easy to configure, and production-ready
Installing Gunicorn
Let's start by installing Gunicorn in your project's virtual environment:
# Activate your virtual environment first
# For example:
# source venv/bin/activate  # On Linux/Mac
# venv\Scripts\activate     # On Windows
pip install gunicorn
Make sure to add Gunicorn to your requirements.txt file:
pip freeze > requirements.txt
Basic Gunicorn Configuration with Flask
Step 1: Prepare Your Flask Application
Your Flask application should be structured in a way that separates the Flask instance from the run command. Here's a common structure:
myapp/
  ├── app/
  │     ├── __init__.py  # Contains your Flask application instance
  │     ├── routes.py
  │     ├── models.py
  │     └── ...
  ├── wsgi.py           # Entry point for Gunicorn
  └── requirements.txt
Inside app/__init__.py, create your Flask application:
from flask import Flask
def create_app():
    app = Flask(__name__)
    # Configure your app
    
    from app.routes import main_bp
    app.register_blueprint(main_bp)
    
    return app
Step 2: Create a WSGI Entry Point
Create a wsgi.py file in the root of your project:
from app import create_app
app = create_app()
if __name__ == "__main__":
    app.run()
This file serves as the entry point for Gunicorn.
Step 3: Run Your Application with Gunicorn
Now you can run your Flask application using Gunicorn:
gunicorn wsgi:app
This command tells Gunicorn to look for the app object inside the wsgi.py file.
By default, Gunicorn will:
- Listen on localhost (127.0.0.1) at port 8000
- Run with a single worker process
- Not daemonize (run in the foreground)
Advanced Gunicorn Configuration
You can customize Gunicorn's behavior using command-line arguments:
Setting Worker Processes
The number of worker processes determines how many concurrent requests your application can handle:
gunicorn --workers=4 wsgi:app
A common formula for determining the optimal number of workers is (2 × number_of_cores) + 1.
Binding to a Different Host/Port
To make your application accessible from other machines or use a specific port:
gunicorn --bind=0.0.0.0:8000 wsgi:app
Using Worker Classes
Gunicorn supports different types of worker processes:
gunicorn --worker-class=gevent wsgi:app
Common worker classes include:
- sync(default): Standard synchronous workers
- gevent: For asynchronous workers with gevent
- eventlet: For asynchronous workers with eventlet
Running as a Daemon
For production, you'll often want to run Gunicorn as a background process:
gunicorn --daemon --workers=4 --bind=0.0.0.0:8000 wsgi:app
Using a Configuration File
For complex configurations, it's better to use a configuration file:
Create a file named gunicorn_config.py:
# Gunicorn configuration file
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gevent"
max_requests = 1000
timeout = 30
keepalive = 2
errorlog = "logs/error.log"
accesslog = "logs/access.log"
loglevel = "info"
Then run Gunicorn with:
gunicorn -c gunicorn_config.py wsgi:app
Working with Nginx and Gunicorn
In production environments, Gunicorn is typically used behind a reverse proxy like Nginx. Nginx handles static files, SSL termination, and load balancing, while Gunicorn focuses on running your Python application.
Here's a basic Nginx configuration to work with Gunicorn:
server {
    listen 80;
    server_name yourdomain.com;
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    location /static {
        alias /path/to/your/static/files;
    }
}
Real-World Example: Deploying a Flask Blog
Let's look at a complete example of deploying a simple Flask blog with Gunicorn.
Sample Application Structure
flask_blog/
  ├── blog/
  │     ├── __init__.py
  │     ├── routes.py
  │     ├── models.py
  │     ├── templates/
  │     └── static/
  ├── wsgi.py
  ├── gunicorn_config.py
  ├── requirements.txt
  └── start.sh
Application Code (blog/init.py)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'your-secret-key'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    
    db.init_app(app)
    
    from blog.routes import main
    app.register_blueprint(main)
    
    with app.app_context():
        db.create_all()
        
    return app
WSGI File (wsgi.py)
from blog import create_app
app = create_app()
if __name__ == "__main__":
    app.run(debug=True)
Gunicorn Configuration (gunicorn_config.py)
bind = "0.0.0.0:8000"
workers = 3
worker_class = "sync"
timeout = 120
errorlog = "logs/error.log"
accesslog = "logs/access.log"
loglevel = "info"
Start Script (start.sh)
#!/bin/bash
mkdir -p logs
gunicorn -c gunicorn_config.py wsgi:app
Make the script executable:
chmod +x start.sh
Running the Application
./start.sh
Monitoring and Managing Gunicorn
For a production deployment, you'll want to ensure your Gunicorn process stays running and restarts if it crashes. Several tools can help with this:
Using Systemd
Create a systemd service file /etc/systemd/system/flask-blog.service:
[Unit]
Description=Gunicorn instance to serve Flask blog
After=network.target
[Service]
User=yourusername
Group=yourgroup
WorkingDirectory=/path/to/flask_blog
ExecStart=/path/to/flask_blog/venv/bin/gunicorn -c gunicorn_config.py wsgi:app
Restart=always
[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl enable flask-blog
sudo systemctl start flask-blog
Check the status:
sudo systemctl status flask-blog
Using Supervisor
Supervisor is another popular process manager. First, install it:
pip install supervisor
Create a configuration file /etc/supervisor/conf.d/flask-blog.conf:
[program:flask-blog]
directory=/path/to/flask_blog
command=/path/to/flask_blog/venv/bin/gunicorn -c gunicorn_config.py wsgi:app
autostart=true
autorestart=true
stderr_logfile=/path/to/flask_blog/logs/supervisor.err.log
stdout_logfile=/path/to/flask_blog/logs/supervisor.out.log
user=yourusername
Update supervisor and start the application:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start flask-blog
Performance Tuning
To optimize Gunicorn for production environments:
- 
Worker Type: Choose the right worker type for your application's workload: - sync: For standard, mostly synchronous applications
- geventor- eventlet: For applications with many concurrent requests
 
- 
Worker Count: Follow the formula (2 × CPU cores) + 1as a starting point
- 
Worker Timeout: Adjust the timeout value based on your application's needs gunicorn --timeout 60 wsgi:app
- 
Max Requests: Configure workers to restart after handling a certain number of requests to prevent memory leaks gunicorn --max-requests 1000 wsgi:app
- 
Keepalive: Set a keepalive value to handle keep-alive connections gunicorn --keepalive 5 wsgi:app
Summary
In this tutorial, we've covered:
- Why you need Gunicorn for production Flask deployments
- How to install and configure Gunicorn with your Flask application
- Advanced Gunicorn configuration options including workers, binding, and worker classes
- Integrating Gunicorn with Nginx for a production setup
- A complete real-world example of deploying a Flask blog
- Monitoring and managing Gunicorn with systemd and Supervisor
- Performance tuning tips for production deployments
By using Gunicorn to deploy your Flask applications, you significantly improve performance, security, and stability. This setup forms the foundation of a robust production environment that can handle real-world traffic and demands.
Additional Resources
- Gunicorn Documentation
- Flask Production Deployment
- Nginx Documentation
- Digital Ocean Guide on Flask + Gunicorn + Nginx
Practice Exercises
- Deploy a simple Flask "Hello World" application using Gunicorn
- Configure Gunicorn with 4 worker processes and gevent worker class
- Create a systemd service file for your Flask application
- Set up Nginx as a reverse proxy in front of your Gunicorn server
- Create a monitoring script that checks if your Gunicorn process is running and sends an alert if it's not
💡 Found a typo or mistake? Click "Edit this page" to suggest a correction. Your feedback is greatly appreciated!