Flask App Hosting Continued

Next we want to copy over our local flask app to the server. You could do this with scp but I prefer to create a deploy folder and bare git repo on the server so I can simply 'git push production master' to deploy to production.

Ensure your pre-commit local git hook is configured:

touch ~/yourproject/.git/hooks/pre-commit
chmod +x ~/yourproject/.git/hooks/pre-commit
vi ~/yourproject/.git/hooks/pre-commit
#!/bin/sh
set -eu

PROJECT_NAME="yourprojectname"
TARGET="$HOME/path/to/your/$PROJECT_NAME"

# Activate virtual env
source "$TARGET"/venv/bin/activate

# Update requirements.txt
pip freeze > "$TARGET"/requirements.txt

# Migrate DB
export FLASK_APP=run.py
flask db migrate
flask db upgrade

# Add changes to commit
git add -A

SSH into your server:

ssh <usermame>@<server-ip>

Create deploy folder:

mkdir ~/deploy-folder

Init a python virtual environment and install wheel via pip:

sudo apt-get install python3-venv
cd ~/yourproject
python3 -m venv venv
source venv/bin/activate
pip install wheel

Add a bare git repo for our hooks:

git init --bare ~/yourproject.git

Add a post-receive file and make it executable:

touch ~/yourproject.git/hooks/postreceive
chmod +x ~/yourproject.git/hooks/post-receive

This post-receive file should contain:

#!/bin/bash
set -eu

PROJECT_NAME="yourproject"
TARGET="$HOME/$PROJECT_NAME"
GIT_DIR="$HOME/$PROJECT_NAME.git"
BRANCH="master"

while read oldrev newrev ref
do
        # only checking out the master (or whatever branch you would like to deploy)
        if [[ $ref = refs/heads/"$BRANCH" ]];
        then
                echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
                git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f
                source $TARGET/venv/bin/activate
                pip install -r $TARGET/requirements.txt
                export FLASK_APP=run.py
                cd $TARGET
                flask db upgrade
                sudo supervisorctl restart $PROJECT_NAME
        else
                echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."
        fi
done

Now we have the server set to receive the branch, we can add the remote on our local machine:

cd ~/path/to/working-copy/
git remote add production yourusername@yourserverip.com:yourproject.git

Copy your local sqlite.db file from local to the server, because for obvious reasons this is not tied to version control.

scp ~/yourproject/dbname.db yourusername@yourserverip:~/yourproject

Copy your local /etc/projectname-config.json to the server's /etc/

On your local:

sudo cp /etc/yourproject-config.json ~/
scp ~/yourproject-config.json yourusername@yourserverip:~/

On the server:

sudo cp ~/yourproject-config.json /etc/

Make some changes back on local and push them via:

git push production master

Your project will now be found in the server's home directory.

On the server open up port 5000:

sudo ufw allow 5000

Serve your flask app:

cd ~/yourproject
source venv/bin/activate
export FLASK_APP=run.py
flask run --host=0.0.0.0

Your app should now be running at the URL:

http://yourserverip:5000

Confirm that your site functions properly then kill it.

Now we can setup nginx and gunicorn to serve our app:

sudo apt install nginx
cd ~/youproject
source venv/bin/activate
pip install gunicorn
sudo rm /etc/nginx/sites-enabled/default
sudo vi /etc/nginx/sites-enabled/yourproject
server {
        listen 80;
        server_name yourserverip;

        location /static {
                alias /home/yourusername/yourproject/yourproject/static;
        }

        location / {
                proxy_pass http://localhost:8000;
                include /etc/nginx/proxy_params;
                proxy_redirect off;
        }
}

Allow http traffic and close 5000:

sudo ufw allow http/tcp
sudo ufw delete allow 5000
sudo ufw enable
sudo ufw status verbose

Restart nginx:

sudo systemctl restart nginx

Start gunicorn service:

cd ~/yourproject
source venv/bin/activate
gunicorn -w 3 run:app

Your app should now be served at http://yourserverip

Got your flask app up and running? Now we can install supervisor to handle gunicorn:

sudo apt install supervisor
sudo vi /etc/supervisor/conf.d/yourproject.conf
[program:yourproject]
directory=/home/youruser/yourproject
command=/home/youruser/yourproject/venv/bin/gunicorn -w 3 run:app
user=youruser
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/yourproject/yourproject.err.log
stdout_logfile=/var/log/yourproject/yourproject.out.log

Create the log files:

sudo mkdir -p /var/log/yourproject
sudo touch /var/log/yourproject/yourproject.err.log
sudo touch /var/log/yourproject/yourproject.out.log

Restart supervisor:

sudo supervisorctl reload

Now your server will be running even if you close your SSH connection and will reboot if the server restarts, great!

We will stop here... Next time I'll show you how to configure your namecheap domain for your site!