Categories
Programming

Serving Flask app with Nginx

So you have build a Flask application and now you want to server the application in the web. In this tutorial, I’m going to teach how you can do that with Nginx on an Ubuntu platform.

Prerequisites

First I’ve to list down some of the prerequisites.

  • Have a machine(e.g. In the cloud such as DigitalOcean droplets) with your flask application folder
  • The machine is based on Ubuntu(e.g. 18.04 or newer)
  • You already have a domain name and it is configured to point to the machine where you have your Flask application. For this tutorial I’m going to take myapp.com.

Before diving in, let’s clarify the application architecture. Following diagram shows you the main components.

In this tutorial, my focus is for the rightmost three items of the diagram. Let’s clarify them briefly.

Nginx / NGINX

Nginx is the webserver in our virtual machine. When the browser requests myapp.com, Nginx will capture the request, process it and send the response(the web page) back to the browser.

uWSGI

UWSGI helps to glue Nginx and the Flask app together.

Flask app

Your Flask application needs to be in the machine.

Installing necessary Ubuntu packages

First make sure the package listing is updated.

sudo apt update

After that, let’s install Python packages and Nginx.

sudo apt install python3-dev python3-pip nginx

I’m going to assume now Nginx server is running in the machine without any issue. If there is an issue, see Troublehsooting section below.

Let’s assume our Flask application directly is located in /var/www/myapp. The application directly structure might look something like following.

Single application

- myapp
  - myapp.py
  ...

Module application

- myapp
  - myapp
    - _init_.py
    ...
  ...

Eitherway, first let’s cd into the application folder.

cd /var/www/myapp

We are going to first build a Python virtual environment to install the Python packages. If you don’t need a virtual environment, you can skip this part. In that case, you would need to substitute python to python3 and pip to pip3 for the commands.

python3 -m venv venv # creates a folder called venv which contains the virtual environment

Now use the virtual environment in the terminal. Once you do that, your terminal line should start with (venv)....

source venv/bin/activate

Install following pip packages. You may also add the other Python packages you need for your application.

pip install wheel uwsgi flask
uWSGI configuration

Now we have to create a file to server as the entry point for uWSGI. This is going to be a Python file called wsgi.py in under your root application folder. The idea is to initialize your application instance and set it to app variable.

# wsgi.py

from myapp import app

# if you use a method to create the app instance, 
# call the method here and set the return value to app variable.

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

The next step is to create a uWSGI configuration file and use wsgi.py file. Let’s create a file called myapp.ini under the root directory of the application folder with following content.

[uwsgi]
module = wsgi:app

master = true
processes = 4

socket = myapp.sock
chmod-socket = 660
vacuum = true

die-on-term = true

The interesting line is module = wsgi:app. Here we tell which module(that we created previously wsgi.py) and the application variable name. More about the configuration options can be found here.

Next, let’s create the systemd service unit file. We will call our file myapp.service. Also I’m using admin as the user name which is going to take the ownership of the service process. In your case, it would be the user that you used to login to the machine and currently using.

sudo vim /etc/systemd/system/myapp.service

Put the following code in the file. Replace admin with your user’s username. Also change myapp to the name of your application to match the paths and description. Also note that you can pass environment variables through the Environment directive (e.g. Environment="MYAPP_ENV_VAR1=xxx").

Other configuration options can be found here.

[Unit]
Description=uWSGI instance to serve myapp
After=network.target

[Service]
User=admin
Group=www-data
WorkingDirectory=/var/www/myapp
Environment="MYAPP_ENV_VAR1=xxx"
Environment="PATH=/var/www/myapp/venv/bin"
ExecStart=/var/www/myapp/venv/bin/uwsgi --ini myapp.ini

[Install]
WantedBy=multi-user.target

We can now start the uWSGI service we created and enable it so that it starts at boot:

sudo systemctl start myapp
sudo systemctl enable myapp

Let’s check the status:

sudo systemctl status myapp
Nginx Configuration

Now we have to tell Nginx to create an entry for our myapp.com use the service.

Let’s create a server configuration file called myapp.

sudo vim /etc/nginx/sites-available/myapp

Then paste the following in the config file. Replace the server name with your own domain and also replace myapp in the path.

server {
    listen 80;
    server_name myapp.com;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/var/www/myapp/myapp.sock;
    }
}

Let’s enable the server configuration and restart the server.

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled
sudo systemctl restart nginx

That should be it. Now when you hit your address in the browser, your Flask application should respond. If you see any errors on the page, don’t worry. See the troubleshooting section.

If you had any difficulty when following the tutorial, let me know. I will try to respond as I can. Also any corrections, improvements and suggestions are welcome.

Troubleshooting

Ensure Nginx is running. Let’s verify that:

systemctl status nginx

See here for Nginx troubleshooting guide.

The logs are the first point of reference when it comes to debugging. Following are the important log locations to debug your problem. You can use tail command to follow the log file and refresh the browser page (to send a request to the web server) and see where the error happens.

By default, Nginx writes its events in two types of logs – the error log and the access log. More about Nginx debugging can be found here.

# Nginx error logs
sudo tail -f /var/log/nginx/error.log

# Nginx access logs
sudo tail -f /var/log/nginx/access.log

Also we have Nginx and our Flask app services running by the service manager. We can use journalctl to query the logs produce by these services.

# Nginx process logs
sudo journalctl -u nginx -f

# Follow Flask app’s uWSGI logs
sudo journalctl -u myapp.service -f

Hopefully with the above logs, you should be able to figure out where exactly your problem is. If you notice that a bug occurred due to missing steps or misinformation, kindly let me know. I will update the tutorial.

0 0 vote
Article Rating

By Sinaru Gunawardena

Sinaru is a software developer at work and a painter in free time. He enjoys writing tech notes on his personal website. He currently lives in Dublin, Ireland.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments