Deploying Production Flask Applications with uWSGI and Nginx

I fell in love with Flask a long time ago when I started to get serious about web development. It is a beautiful framework that gives enough control to allow you to do what you want, while still enforcing proper web programming paradigms. The problem though is that the Flask web server is no where near production ready. In fact, if you were to deploy with the Flask web server not only will your application be painfully slow, but it will also crash constatly. To solve this problem we have to take the job of handling requests away from Flask, and allow it to only dictate the logic of the application. To do this we must use a WSGI such as Gunicorn or uWSGI. In this post I will be discussing how we can deploy a production web application with uWSGI and Nginx.

We will start out by creating a basic Flask application, a simple "Hello World" app.

#app.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():  
    return "Hello World!"

if __name__ == "__main__":  
    app.run(debug=True)

Great! Now if we run app.py and head over to http://localhost:5000 "Hello World" will be displayed.

Now that we have our app, we need to install uWSGI and Nginx. I am assuming that you are on Ubuntu, or a Debian based system because we will be using apt-get to install this software.

sudo apt-get install uwsgi nginx screen  

Just as a side note, we do not technically need Nginx to launch an application with uWSGI. The reason we use it though is because it makes tasks such as adding HTTPS a breeze, but that will not be covered in this post.

Now that we have uWSGI and Nginx installed we need to setup Nginx to work with uWSGI.

We need to edit the Nginx config so that it will play nicely with uWSGI. Replace your /etc/nginx/nginx.conf file with the configuration below.

user root;  
worker_processes 8;  
events {  
    worker_connections  1024;
}


http {  
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    gzip  on;

    server {
        listen       80;
        server_name  *.yourdomain.com yourdomain.com;
        location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:7070;
        }
    }
}

Replace "yourdomain.com" with the domain that you will be using for this application. If you do not have a domian yet you could also put the IP address of the server.

Next we need to restart nginx using this command:

sudo service nginx restart  

We are almost done! All that is left now is deploy the uWSGI instance. Start by making sure that you are in the same directory as the app.py that we just made. Once you are there, execute the following command:

screen uwsgi --socket 127.0.0.1:7070 --processes 5 --module app --callable app  

This will launch uWSGI in a screen session on port 7070, which is the port that we configured Nginx to pass requests to.

Tada! Like that we have deployed Flask in a way that is now ready for production.

So, we could have very easily used Gunicorn to perform this same task, but I prefer uWSGI and here is why.

uWSGI is written in C and is built for performance. Gunicorn is written in Python thus is slower. I have used both of these technologies in my jobs and I have found that every time uWSGI out performs Gunicorn, and even NodeJS at times.

With uWSGI logging is much less of a pain than with Gunicorn.

uWSGI has been around longer than Gunicorn so the project is more mature.

At the end of the day though, both of these technologies do the same thing, and both can be used in a production setting.

Good luck.

Frankie