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.
```
- myapp
- myapp.py …
<p>
</p>
</div>
<div class="is-layout-flow wp-block-column">
<p>
Module application
</p>
```
- myapp
- myapp
- _init_.py
...
...
<p>
</p>
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.