Flask Production Server
Introduction
When you're developing a Flask application, you typically use the built-in development server that comes with Flask. This server is perfect for development but has limitations that make it unsuitable for production environments:
- It's single-threaded and can only handle one request at a time
- It lacks security features needed in production
- It's not optimized for performance or stability
- Flask itself warns against using it in production with the message: "Do not use the development server in a production environment"
In this tutorial, you'll learn how to set up a production-ready server for your Flask application using industry-standard tools that provide better performance, security, and reliability.
Why Use a Production Server?
Before diving into implementation, let's understand why a production server is essential:
- Performance: Production servers can handle multiple concurrent requests efficiently
- Reliability: They're designed to run continuously without downtime
- Security: They include security features to protect against common web vulnerabilities
- Scalability: They can be configured to scale as your application grows
WSGI - The Bridge Between Flask and the Web
WSGI (Web Server Gateway Interface) is a specification that describes how a web server communicates with web applications. Flask applications need a WSGI server in production to handle requests properly.
Popular WSGI servers include:
- Gunicorn (Green Unicorn)
- uWSGI
- Waitress (Windows-friendly)
For this tutorial, we'll focus on Gunicorn, which is widely used in the Python community.
Setting Up a Production Server with Gunicorn and Nginx
We'll create a production setup using:
- Gunicorn: A WSGI HTTP server for running the Flask application
- Nginx: A high-performance web server that acts as a reverse proxy
This is a common and robust setup used by many production Flask applications.
Step 1: Install Required Packages
First, make sure you have your Flask application ready. Then install Gunicorn:
pip install gunicorn
Step 2: Create a WSGI Entry Point
Create a file named wsgi.py
in your project's root directory:
from your_app_name import app
if __name__ == "__main__":
app.run()
Replace your_app_name
with the name of your Flask application.
Step 3: Run Your Application with Gunicorn
Now you can start your Flask application with Gunicorn:
gunicorn --workers=3 --bind=0.0.0.0:8000 wsgi:app
Here's what each part means:
--workers=3
: Run 3 worker processes (typically 2x number of CPU cores + 1)--bind=0.0.0.0:8000
: Listen on all network interfaces on port 8000wsgi:app
: Use theapp
object from thewsgi.py
file
Your Flask application is now running with Gunicorn and can handle multiple requests concurrently!
Step 4: Install and Configure Nginx
While Gunicorn is much better than Flask's development server, using Nginx in front of it adds even more benefits:
- Efficient serving of static files
- Load balancing
- SSL/TLS termination
- Protection against certain types of attacks
First, install Nginx:
# For Ubuntu/Debian
sudo apt-get update
sudo apt-get install nginx
# For CentOS/RHEL
sudo yum install epel-release
sudo yum install nginx
Step 5: Configure Nginx to Proxy Requests to Gunicorn
Create a new Nginx configuration file:
sudo nano /etc/nginx/sites-available/flask_app
Add the following configuration:
server {
listen 80;
server_name your_domain.com www.your_domain.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;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static {
alias /path/to/your/static/files;
expires 30d;
}
}
Create a symbolic link to enable the site:
sudo ln -s /etc/nginx/sites-available/flask_app /etc/nginx/sites-enabled/
Test the Nginx configuration and restart it:
sudo nginx -t
sudo systemctl restart nginx
Now, visitors to your domain will be served by Nginx, which forwards requests to your Gunicorn server running the Flask application.
Running Gunicorn with Systemd for Persistence
To ensure your application starts automatically after server reboots, you can create a systemd service:
sudo nano /etc/systemd/system/flask_app.service
Add the following content:
[Unit]
Description=Gunicorn instance to serve Flask application
After=network.target
[Service]
User=username
Group=username
WorkingDirectory=/path/to/your/app
Environment="PATH=/path/to/your/venv/bin"
ExecStart=/path/to/your/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app
[Install]
WantedBy=multi-user.target
Replace username
and paths with your actual values. Then enable and start the service:
sudo systemctl enable flask_app
sudo systemctl start flask_app
Check its status:
sudo systemctl status flask_app
Real-World Example: Deploying a To-Do List Application
Let's walk through deploying a simple To-Do List Flask application to production.
The Flask Application
Here's our example application (app.py
):
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todos.db'
db = SQLAlchemy(app)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
task = db.Column(db.String(200), nullable=False)
completed = db.Column(db.Boolean, default=False)
@app.route('/')
def index():
todos = Todo.query.all()
return render_template('index.html', todos=todos)
@app.route('/add', methods=['POST'])
def add_todo():
task = request.form.get('task')
new_todo = Todo(task=task)
db.session.add(new_todo)
db.session.commit()
return redirect(url_for('index'))
@app.route('/complete/<int:todo_id>')
def complete(todo_id):
todo = Todo.query.get_or_404(todo_id)
todo.completed = not todo.completed
db.session.commit()
return redirect(url_for('index'))
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
Create WSGI Entry Point
Create wsgi.py
:
from app import app
if __name__ == "__main__":
app.run()
Deployment Steps
-
Upload your code to your server (using Git, SCP, or SFTP)
-
Create a virtual environment and install dependencies:
bashpython -m venv venv
source venv/bin/activate
pip install flask flask_sqlalchemy gunicorn -
Initialize the database:
bashpython -c "from app import app, db; app.app_context().push(); db.create_all()"
-
Test with Gunicorn:
bashgunicorn --bind 127.0.0.1:8000 wsgi:app
-
Create systemd service file as shown earlier
-
Configure Nginx as shown earlier
-
Start services:
bashsudo systemctl start flask_app
sudo systemctl restart nginx
After completing these steps, your To-Do List application should be accessible via your domain, running on a production-ready server setup.
Best Practices for Flask in Production
To make your Flask application truly production-ready, consider these additional best practices:
-
Use environment variables for configuration:
pythonimport os
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'fallback-key-for-dev') -
Implement proper logging:
pythonimport logging
from logging.handlers import RotatingFileHandler
if not app.debug:
file_handler = RotatingFileHandler('app.log', maxBytes=10240, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Application startup') -
Use HTTPS by configuring SSL/TLS in Nginx (you can use Let's Encrypt for free certificates)
-
Implement rate limiting to prevent abuse:
bashpip install flask-limiter
pythonfrom flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
) -
Monitor your application using tools like Prometheus, Grafana, or simpler tools like Datadog or New Relic
Summary
In this tutorial, you've learned how to deploy a Flask application to a production environment using industry-standard tools:
- Why the Flask development server is not suitable for production
- How to use Gunicorn as a WSGI server for better performance and reliability
- How to set up Nginx as a reverse proxy for added security and features
- How to ensure your application runs persistently using systemd
- Best practices for running Flask applications in production
By following these steps, you've transformed your development Flask application into a robust, production-ready web service that can handle real-world traffic efficiently and securely.
Additional Resources
- Gunicorn Documentation
- Nginx Documentation
- Flask Deployment Options
- Digital Ocean's Flask Deployment Guide
Exercises
- Deploy a simple Flask application using the methods described in this tutorial.
- Configure your production server to handle static files efficiently.
- Implement HTTPS using Let's Encrypt.
- Set up a monitoring solution for your Flask application to track performance metrics.
- Implement a CI/CD pipeline that automatically deploys your Flask application when you push to a Git repository.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)