Deploying a Flask Application in Production (Nginx, Gunicorn, and PM2)

My first post on this blog went over how to deploy a Flask application in production using Nginx, uWSGI, and screen. The problem with this approach is that screen is never a good approach to take when deploying production applications. Also, uWSGI, though great, is not the only WSGI out there. Most of the time people tend to use gunicorn for Python applications. For these reasons I am writing this post. It's going to be similar to the first one, but will hopefully show that there are many ways of doing the same thing, and it is ultimately up to you to descide how you plan to design your application and what the right tools for the job are at the time.

With that said, let's get started!

First we are going to need to install all of the software that we will be using here. Since I am using Ubuntu, I am going to use apt-get for this:

sudo apt-get intall nginx python-pip nodejs npm  
sudo pip install flask gunicorn  
sudo npm install pm2  

Now we need an application... so let's create a simple "Hello World!" Flask application.

from flask import *

app = Flask(__name__)

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

if __name__ == "__main__":  
    app.run(port=8080)

Ahh, isn't Flask beautiful? Now that we have our application, let's configure Nginx.

Since we will be using Gunicorn as our WSGI we are going to have to configure Nginx to be a reverse proxy to our application. Lucky for us, this is actually very simple.

In /etc/nginx/sites-available/default we'll need to add the following code:

server {  
    listen 80;
    listen [::]:80;
    server_name www.mydomain.com mydomain.com;
    location / {
        proxy_pass http://127.0.0.1:8080;                                                                                                                                
    }
}

Be sure to change "mydomain.com" with the domain that you own for this application. With this kind of configuration Nginx will allow us to host many different websites on the same server, which is just an extra plus. A final sudo service nginx restart and Nginx will be good to go.

Now that we have Nginx up and running we need to deploy the application in a way that will make it resilient to crashes and server restarts. For this we will use PM2. In the past I was a firm beilieve in using Upstart scripts, but I have come to realize that they are more trouble than they are worth, and very much outdated. Ubuntu has actually gotten rid of them in more recent versions anyway, so it's no longer a viable option. There is no need to fear however! There is an application out there called PM2, which is a process manager meant for NodeJS applications. PM2 isn't only limited to NodeJS though, we can very easily use it for our purposes here with a little bit of bash magic.

To get PM2 to work with our application we'll need to create a bash script. All this bash script will contain is the command that we will use to start our application.

Let's save the previous Python code in a file called, helloworld.py and let's create a file called "startsite.sh". In that file we will add the following code:

gunicorn -w 10 hello_world:app  

What this script is going to do is start our application with Gunicorn with 10 workers. The reason for the bash script is that PM2 cannot execute these commands directly, but can execute bashs scripts.

Now we'll need to start the application with PM2.

pm2 start start_site.sh  

The site is now up and running! If you head over to "http://mydomain.com" you should see "Hello World!" on the screen!

There are still one final step that we need to take to ensure that pm2 will restart our application if the server restarts.

pm2 startup  
pm2 save  

And like that we are done! See? That was really easy. Deployment doesn't need to be this overly complicated process.

Good Luck.

Frankie