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 workersgevent
: For asynchronous workers with geventeventlet
: 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 applicationsgevent
oreventlet
: For applications with many concurrent requests
-
Worker Count: Follow the formula
(2 × CPU cores) + 1
as a starting point -
Worker Timeout: Adjust the timeout value based on your application's needs
bashgunicorn --timeout 60 wsgi:app
-
Max Requests: Configure workers to restart after handling a certain number of requests to prevent memory leaks
bashgunicorn --max-requests 1000 wsgi:app
-
Keepalive: Set a keepalive value to handle keep-alive connections
bashgunicorn --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
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)