Vue.js + Flask + Flask-SQLAlchemy: Part 3: deployment on DigitalOcean

Change to Frontend:

TH Zhao
4 min readJun 10, 2019

Before deploying, we need to change API address for frontend, as the http://localhost will now mean the user’s computer rather than our API server.

Environment variables can be handy to automatically change this for frontend when in development and built.

In a web app, we most likely have to access a backend API server through a URL. This URL can be something like http://localhost:8080/api while in development, and https://site.com/api in production when the project is deployed.

To do this, create two files at backend/

In .env.development

VUE_APP_API = ‘http://127.0.0.1:5000'

and in .env.production

VUE_APP_API = ‘http://www.domain.com/API'

Changes to Backend:

Flask app doesn’t need many changes.

In http://flask.pocoo.org/docs/dev/quickstart/, to run externally visible server, we need to 1. have the debugger disabled or trust the users on your network, 2. adding--host=0.0.0.0 to the command line:

So we change our app.run()

to app.run(host=’0.0.0.0', debug=False)

Now that we have everything ready, let’s get the real stuff done!

  1. Register an account on digitalocean and create a droplet
  2. Access via the SSH (on windows it will be Putty) with your IP address (165.227.139.xxx). First time to log in you may need to reset the root password.
  3. for the pipenv, I find it is hard to use in deployment. Let’s convert the pipfile into requirements.txtand with pipenv lock -r then we can use pip install -r requirements.txt --user
# Add user and give superuser
adduser user
usermod -aG sudo user# Firewall
ufw allow OpenSSH
ufw enable
# install nginx and python and npm
sudo apt update
sudo apt install nginx nodejs npm python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools
sudo ufw allow 'Nginx Full'# git clone our repo
git clone https://github.com/herbzhao/vue-flask-sqlalchemy.git
# optional, to update the existing directory
# git pull origin master
cd vue-flask-sqlalchemy/backend/
pip install uwsgi --user
pip install -r requirements.txt --user

# test the code
python app.py
  • We need a wsgi.py file, I also changed the app.py to api.py to avoid conflicts
from app import appif __name__ == "__main__":
app.run()

Configure the uWSGI to run on the boot up

creat new backend/app.ini

[uwsgi]
module = wsgi:app
master = true
processes = 4
socket = app.sock
chmod-socket = 660
vacuum = true
die-on-term = true

app.sock will be the socket file for nginx communication.

  • To set up systemd for automatically running Nginx on bootup

sudo nano /etc/systemd/system/api.service

[Unit]
Description=uWSGI Python container server
After=network.target
[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/vue-flask-sqlalchemy/backend
Environment=”PATH=/home/user/.local/bin”
ExecStart=/home/user/.local/bin/uwsgi — ini app.ini
[Install]
WantedBy=multi-user.target

Configure the gunicorn to run on the boot up

Gunicorn is a Python Web Server Gateway Interface HTTP server that should be much better than the flask. The Web Server Gateway Interface (WSGI) is a simple calling convention for web servers to forward requests to web applications or frameworks written in the Python programming language.

  • To set up systemd for automatically running Gunicorn on bootup

sudo nano /etc/systemd/system/api.service

[Unit]
Description=Gunicorn instance to serve api
After=network.target
[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/vue-flask-sqlalchemy/backend
ExecStart=/home/user/.local/bin/pipenv run gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target

Then we can start the service

sudo systemctl daemon-reload

sudo systemctl start api

sudo systemctl enable api

sudo systemctl status api

Configuring Nginx to Proxy Requests

Our Gunicorn application server should now be up and running, waiting for requests on the socket file in the project directory.

I will utilize Nginx to serve static content such as HTML, CSS, and JavaScript as well as to reverse proxy REST API calls to the Flask / uWSGI application.

sudo nano /etc/nginx/sites-available/vueflask :

server {
listen 80;
server_name 1.2.3.4;
location /api {
include uwsgi_params;
uwsgi_pass unix:/home/user/vue-flask-sqlalchemy/backend/app.sock;
}
location / {
root /home/user/vue-flask-sqlalchemy/frontend/dist;
try_files $uri $uri/ /index.html;
}
}

Then we just copy the sites-available to sites-enabled

sudo ln -s /etc/nginx/sites-available/vueflask /etc/nginx/sites-enabled

Very important! the api is now served at /api route, so we either find someway to mount our api or an easier solution is to modify the flask app. Changing all the route from /todos → /api/todos in Flask.

With the file in that directory, you can test for syntax errors:

sudo nginx -t

If everything is alright, let’s restart the nginx and then we can browse our website at our IP address:

sudo systemctl restart nginx

Woala!!!

Bonus: using a domain name

  1. Add domain.com to your digitalocean control panel.
  2. Changing the name servers on the domain name registrar with ns1.digitalocean.com ns2.digitalocean.com ns3.digitalocean.com
  3. Create new record (1 with @, another with www).

You have to change Nginx setting as well.

sudo nano /etc/nginx/sites-available/vue-flask

server {  
listen 80;
server_name smithtechs.co.uk www.smithtechs.co.uk;
location /api {
include uwsgi_params;
uwsgi_pass unix:/home/user/vue-flask-sqlalchemy/backend/app.sock;
}
location / {
root /home/user/vue-flask-sqlalchemy/frontend/dist;
try_files $uri $uri/ /index.html;
}
}
server {
listen 80;
server_name 165.227.139.201;
location /api {
include uwsgi_params;
uwsgi_pass unix:/home/user/vue-flask-sqlalchemy/backend/app.sock;
}
location / {
root /home/user/vue-flask-sqlalchemy/frontend/dist;
try_files $uri $uri/ /index.html;
}
}

sudo ln -s /etc/nginx/sites-available/vue-flask /etc/nginx/sites-enabled

sudo systemctl restart nginx

DEBUG:

If you encounter any errors, trying checking the following:

  • sudo less /var/log/nginx/error.log: checks the Nginx error logs.
  • sudo less /var/log/nginx/access.log: checks the Nginx access logs.
  • sudo journalctl -u nginx: checks the Nginx process logs.
  • sudo journalctl -u api: checks your Flask app's Gunicorn logs.

HTTPS encryption

Our vuejs frontend needs https connection to work.

sudo add-apt-repository ppa:certbot/certbot

sudo apt install python-certbot-nginx

sudo systemctl reload nginx

sudo certbot --nginx -d example.com -d www.example.com

sudo certbot renew — dry-run

--

--