Flask: Deploying with Gunicorn and Nginx
Deploying a Flask application with Gunicorn and Nginx provides a robust, production-ready setup for serving web applications efficiently and securely. Flask, built on a lightweight core with Jinja2 Templating and Werkzeug WSGI, pairs seamlessly with Gunicorn (a Python WSGI server) for application serving and Nginx (a high-performance web server) for reverse proxying, static file serving, and load balancing. This tutorial covers Flask deploying with Gunicorn and Nginx, providing a step-by-step guide for deployment on a Linux server (e.g., Ubuntu), including configuration, security, and best practices.
01. Why Use Gunicorn and Nginx?
Gunicorn handles the execution of Flask applications, processing dynamic requests efficiently with multiple worker processes, while Nginx acts as a reverse proxy, serving static files, managing SSL/TLS, and balancing load. This combination leverages Flask’s compatibility with Werkzeug WSGI for request handling and Jinja2 Templating for rendering, ensuring high performance, scalability, and security for production environments.
Example: Basic Flask App
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello, Gunicorn and Nginx!'
if __name__ == '__main__':
app.run()
Explanation:
- A minimal Flask app to deploy with Gunicorn and Nginx.
- The development server (
app.run()
) is not used in production.
02. Prerequisites
Before starting, ensure the following are available:
- Linux Server: Ubuntu 20.04 or later (e.g., AWS EC2, DigitalOcean, or local VM).
- Python 3.10+: Installed on the server (
sudo apt install python3 python3-pip python3-venv
). - Git: For cloning the app repository (
sudo apt install git
). - Domain (Optional): For SSL setup with Let’s Encrypt.
- SSH Access: To configure the server.
03. Step-by-Step Deployment Guide
This guide assumes deployment on an Ubuntu 22.04 server. Adjust commands for other Linux distributions as needed.
3.1 Set Up the Server
Example: Initial Server Setup
# Update system and install dependencies
sudo apt update
sudo apt install -y python3 python3-pip python3-venv git nginx
# Create a non-root user (optional, for security)
sudo adduser --gecos "" flaskuser
sudo usermod -aG sudo flaskuser
su - flaskuser
Explanation:
- Installs Python, Git, and Nginx.
- Creates a non-root user for security (recommended but optional).
3.2 Clone the Flask Application
Example: Clone and Set Up App
# Clone repository (replace with your repo URL)
git clone https://github.com/yourusername/flask-app.git
cd flask-app
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install flask gunicorn
pip freeze > requirements.txt
requirements.txt (Generated):
Flask==3.0.3
gunicorn==22.0.0
Jinja2==3.1.4
Werkzeug==3.0.4
...
Explanation:
- Clones the Flask app from a Git repository.
- Sets up a virtual environment and installs
gunicorn
for production.
3.3 Configure Gunicorn
Example: Test Gunicorn
# Test Gunicorn (inside flask-app directory)
gunicorn --workers 3 --bind 0.0.0.0:8000 app:app
Updated app.py with Environment Variables:
# app.py
from flask import Flask
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
@app.route('/')
def index():
return 'Hello, Gunicorn and Nginx!'
if __name__ == '__main__':
app.run()
Explanation:
gunicorn app:app
: Runs the app fromapp.py
with the Flask instanceapp
.--workers 3
: Uses 3 worker processes (adjust based on CPU cores, typically 2*CPU + 1).- Test at
http://<server-ip>:8000
. Stop withCtrl+C
.
3.4 Set Up Gunicorn as a Service
Example: Systemd Service for Gunicorn
# Create systemd service file
sudo nano /etc/systemd/system/flaskapp.service
/etc/systemd/system/flaskapp.service:
[Unit]
Description=Gunicorn instance for Flask app
After=network.target
[Service]
User=flaskuser
Group=www-data
WorkingDirectory=/home/flaskuser/flask-app
Environment="SECRET_KEY=my-secure-key"
ExecStart=/home/flaskuser/flask-app/venv/bin/gunicorn --workers 3 --bind unix:/home/flaskuser/flask-app/flaskapp.sock app:app
[Install]
WantedBy=multi-user.target
Enable and Start Service:
sudo systemctl start flaskapp
sudo systemctl enable flaskapp
sudo systemctl status flaskapp
Explanation:
- Creates a systemd service to run Gunicorn automatically.
- Uses a Unix socket (
flaskapp.sock
) for communication with Nginx. - Sets environment variables directly in the service file (alternatively, use a
.env
file).
3.5 Configure Nginx
Example: Nginx Configuration
# Create Nginx config
sudo nano /etc/nginx/sites-available/flaskapp
/etc/nginx/sites-available/flaskapp:
server {
listen 80;
server_name your-domain.com www.your-domain.com; # Replace with your domain or server IP
location / {
proxy_pass http://unix:/home/flaskuser/flask-app/flaskapp.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /home/flaskuser/flask-app/static/;
}
}
Enable Nginx Config:
sudo ln -s /etc/nginx/sites-available/flaskapp /etc/nginx/sites-enabled
sudo nginx -t # Test config
sudo systemctl restart nginx
Explanation:
- Nginx proxies requests to Gunicorn via the Unix socket.
- Serves static files directly from the
static/
folder. - Replace
your-domain.com
with your domain or server IP.
3.6 Secure with SSL Using Let’s Encrypt
Example: SSL Setup
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain SSL certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Updated Nginx Config (Auto-Updated by Certbot):
server {
listen 80;
server_name your-domain.com www.your-domain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name your-domain.com www.your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location / {
proxy_pass http://unix:/home/flaskuser/flask-app/flaskapp.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /home/flaskuser/flask-app/static/;
}
}
Restart Nginx:
sudo systemctl restart nginx
Explanation:
- Certbot automates SSL certificate issuance and Nginx configuration.
- Redirects HTTP to HTTPS for security.
3.7 Common Deployment Issues
Example: Gunicorn Socket Permission Error
nginx: [emerg] connect() to unix:/home/flaskuser/flask-app/flaskapp.sock failed (13: Permission denied)
Solution:
sudo chown www-data:www-data /home/flaskuser/flask-app/flaskapp.sock
sudo chmod 660 /home/flaskuser/flask-app/flaskapp.sock
sudo systemctl restart flaskapp
sudo systemctl restart nginx
Explanation:
- Ensures Nginx (
www-data
) can access the Gunicorn socket. - Check logs with
sudo journalctl -u flaskapp
orsudo tail -f /var/log/nginx/error.log
.
04. Best Practices
- Environment Variables: Use
.env
withpython-dotenv
or systemd for sensitive data. - .gitignore: Exclude
venv/
,.env
, and*.pyc
.
# .gitignore
venv/
.env
*.pyc
flaskapp.service
(e.g., --access-logfile -
).SESSION_COOKIE_SECURE=True
, and restrict server access (e.g., firewall with UFW).4.1 Comprehensive Example
Example: Full Deployment with Database
# app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SESSION_COOKIE_SECURE'] = True
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
@app.route('/')
def index():
users = User.query.all()
return f'Users: {[user.username for user in users]}'
if __name__ == '__main__':
app.run()
.env File:
SECRET_KEY=my-secure-key
DATABASE_URL=sqlite:///app.db
requirements.txt:
Flask==3.0.3
gunicorn==22.0.0
flask-sqlalchemy==3.1.1
python-dotenv==1.0.1
flaskapp.service:
[Unit]
Description=Gunicorn instance for Flask app
After=network.target
[Service]
User=flaskuser
Group=www-data
WorkingDirectory=/home/flaskuser/flask-app
EnvironmentFile=/home/flaskuser/flask-app/.env
ExecStart=/home/flaskuser/flask-app/venv/bin/gunicorn --workers 3 --bind unix:/home/flaskuser/flask-app/flaskapp.sock app:app
[Install]
WantedBy=multi-user.target
Deployment Commands:
sudo systemctl start flaskapp
sudo systemctl enable flaskapp
sudo ln -s /etc/nginx/sites-available/flaskapp /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx
sudo certbot --nginx -d your-domain.com
Explanation:
- Integrates SQLAlchemy, environment variables, and SSL.
- Runs securely with Gunicorn and Nginx.
05. Common Use Cases
5.1 Deploying a Web Application
Example: Web App with Static Files
# app.py
from flask import Flask, render_template
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
templates/index.html:
<!DOCTYPE html>
<html>
<head><title>Flask App</title></head>
<body><h1>Welcome!</h1><img src="/static/logo.png"></body>
</html>
Explanation:
- Nginx serves
/static/logo.png
efficiently. - Uses Jinja2 Templating for dynamic rendering.
5.2 Deploying an API
Example: Flask API
# app.py
from flask import Flask, jsonify
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
@app.route('/api/data')
def data():
return jsonify({'message': 'API with Gunicorn and Nginx'})
if __name__ == '__main__':
app.run()
Explanation:
- Deploys a RESTful API with Gunicorn handling dynamic requests.
- Nginx proxies API requests securely over HTTPS.
Conclusion
Deploying Flask applications with Gunicorn and Nginx, powered by Jinja2 Templating and Werkzeug WSGI, provides a high-performance, secure, and scalable production environment. Key takeaways:
- Use Gunicorn for WSGI serving and Nginx for reverse proxying and static files.
- Secure with SSL via Let’s Encrypt and environment variables.
- Manage Gunicorn with systemd for reliability.
- Troubleshoot using logs and ensure proper permissions.
This setup ensures Flask applications are production-ready, handling high traffic and securing user data effectively!
Comments
Post a Comment