Rasmus Ähtävä

Hosting a flask app on ubuntu with nginx and gunicorn

Hey, I know, it's very complicated, but I got it running.

In the following commands replace vim with nano if noob:).

Install nginx(your server's load balancer):

sudo apt install nginx

Install python,pip and virtual environment:

sudo apt update
sudo apt install python3 python3-pip python3-venv

If you don't have requirements.txt -> on your local dev machine run this with virtual env active in your app folder pip freeze > requirements.txt .

Get your source code to the server with cloning:

git clone <repository>

or with scp(secure copy). Copy the folder from the local machine with:

scp /folder_to_move <server_user>@<IP_adress>:/where/to/copy/in/server

Go to your app folder, create an virtual environment and activate it:

cd /path/to/your/app && python3 -m venv venv && source venv/bin/activate

Install your dependencies and gunicorn(used to run our app):

pip install -r requirements.txt && pip install gunicorn

Add environment variables needed for your app into:

sudo vim /etc/<your_app_name>_config.json

e.g. in app_config.json:

{
    "SECRET_KEY":"asdf",
}

Then load this file into your app like this:

with open("/etc/app_config.json") as config_file:
    config = json.load(config_file)

secret_key = config.get("SECRET_KEY")

Next create a nginx config for your app:

sudo vim /etc/nginx/conf.d/<your_app_name>.conf

Add the following stuff in there (replace example with your domain/IP):

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Run nginx:

sudo systemctl restart nginx

Our nginx is now listening on port 5000 so we need to make our app run on that port next. Create a new service file which will take care of running your app:

sudo vim /etc/systemd/system/<your_app_name>.service

Add the following stuff in there(edit when asked):

[Unit]
Description=Something here about your app, doesn't matter
After=network.target

[Service]
User=<your_server_username>
Group=<your_server_username>
WorkingDirectory=/<path_to_your_app>
ExecStart=/<path_to_your_app>/venv/bin/gunicorn -b 0.0.0.0:5000 -w 3 --env <ENVIRONMENT_VARIABLE_NAME>="this_is_the_value_but_you_should_put_env_variables_here_that_are_needed_WHEN_you_RUN_the_app_and_others_should_go_to_json_file_in_/etc/-folder" <your_filename_where_flask_app_var_is_like_"app">:<your_flask_app_var_name_like_"app">
Restart=always

[Install]
WantedBy=multi-user.target

There we said to bind -b our app to run on localhost(0.0.0.0 is same as 127.0.0.1) and use port 5000, also we said -w 3 which meant we want 3 workers to be running our app. Determine how many workers you need in your machine: #cores * 2 + 1.

Load the service file:

sudo systemctl daemon-reload

Make our service file run in the background:

sudo systemctl restart <your_app_name>.service

Now our app should be running! You can check the status by:

sudo systemctl status <your_app_name>.service

When further developing your app I recommend using github and cloning the repo like we did above, then follow these steps after making changes to github:

-> login to your server

pull the changes:

cd /path/to/app && git pull && cd

reload the service:

sudo systemctl restart <your_app_name>.service

There you go:)