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 applicationsgeventoreventlet: 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
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 feedback@compilenrun.com. I’d greatly appreciate your feedback! :)