Serving python apps with nginx and gunicorn
0. Intro
In order to serve a python web app hosted on a server there are some tools that you need to install and set up.
1. Basic schema
First of all you will need a Linux machine. Iโd suggest you use an AWS EC2 instance. You can see Creating EC2 instances in AWS how to create one.
There are three main components needed for serving a python app:
- Web server
- WSGI
- Python app
- Supervisor
1.1 Web server
This component will handle the requests and among other things it will deal with static files (images, css, jsโฆ). In this post we are going to use Nginx since it is more commonly used for python. Another popular option is Httpd.
1.2. WSGI
Web servers cannot communicate directly with python apps so you need a Web Server Gateway Interface (WSGI). In this case we will use Gunicorn because it is a simple solution. Another common choice would be Uwsgi.
1.3. Python App
The idea of this post is to set up nginx
and gunicorn
. To test them you can create a very simple app using Flask. You can do the same with Django or Dash apps.
1.4. Supervisor
Supervisor is a system that allows users to monitor and control processes. This will make sure to restart the gunicorn process if it anything goes wrong. Without it you should do it yourself.
You can read more about Why a web server and wsgi.
2. Installation
First you should create the flask app and test it. After you will install and configure both wsgi and supervisor in order to have a service to serve the app. Finally you will configure nginx to serve the content to the web.
2.1. Python app
First letโs create the index.py
with the app:
# Create html folder if neeeded
mkdir /var/www/html
cd /var/www/html
# Install requirements
pip3 install flask
# Create index.py with the app
nano /var/www/html/index.py
And paste the following code:
/var/www/html/index.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World"
if __name__ == '__main__':
app.run(debug=True)
You can try it by running:
python3 index.py
You can now view it by going at XX.XXX.XXX.XXX:PORT
(Use your EC2 IP and the port).
Default flask port is 5000. To try it you need to open the port.
2.1.1. Open EC2 ports (optional)
If you are using and AWS EC2 it is possible that you donโt have the port 80 opened. You can do it with:
- Go to AWS console and then to the EC2 page. Use the sidebar to go to
NETWORK & SECURITY/Security Groups
. - Find the security group of your EC2 instance and edit the Inbound rules.
- Add
Custom TCP Rule
with port5000
(or the one you used). - If you are opening a port for testing, remeber to close it back when you are done.
2.2. WSGI
Install gunicorn with:
pip3 install gunicorn
2.3 Install supervisor
sudo apt install supervisor -y
You will create an config file for your project, for example /etc/supervisor/conf.d/flask_test.conf
:
sudo nano /etc/supervisor/conf.d/flask_test.conf
And write the following configuration:
/etc/supervisor/conf.d/flask_test.conf
[program:flask_test]
command = gunicorn index:app -b localhost:8000
directory = /var/www/html/
autostart = true
autorestart = true
index
refers to the python file. app
is the name of the flask app that is inside this file.
Then start supervisor:
sudo supervisorctl reread
sudo service supervisor restart
# Check the result
sudo supervisorctl status
2.4. Web server
2.4.1. Install and test nginx
To install nginx run:
sudo apt-get update
sudo apt install nginx -y
You can now view if nginx is serving content by going at XX.XXX.XXX.XXX (Use your EC2 IP). You will see the nginx
default landing page:
You will need the port 80 opened
2.4.2 Configure nginx
You should add a config file for your app (flask_test
for example) inside the nginx sites-available
folder. You should delete the default
config file.
sudo rm /etc/nginx/sites-available/default
sudo nano /etc/nginx/sites-available/flask_test
And write the following configuration:
/etc/nginx/sites-available/flask_test
server {
listen 80 flask_server;
location / {
proxy_pass http://127.0.0.1:8000;
}
}
8000
should match the port defined at supervisor and 80
the port that you opened to test nginx.
Now you need a symlink in sites-enabled
that points to the file you created before:
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/flask_test /etc/nginx/sites-enabled
And now you can start nginx:
sudo nginx -t
sudo service nginx restart
You can now go to XX.XXX.XXX.XXX
and you should see the flask app Hello World
message.
If everything is working remeber to close the ports that you opened only for testing.
3. Next steps
3.1. Configure nginx
The nginx configuration you used is really simple. For example you can set nginx to serve static files directly instead of passing through the flask app by:
/etc/nginx/sites-available/flask_test
server {
listen 80 flask_server;
location /static/ {
root /var/www/html/;
}
location / {
proxy_pass http://127.0.0.1:8000;
}
}
For more info about How to configure nginx.
3.2. SSL certificate
I strongly suggest you set up a SSL certificate for apps that are in production. You can learn how with How to secure nginx with let's encrypt.
While working on this post I set this app for my first time. It is normal to encounter difficultes but I was able to sort it out, you can do it too!