How to create a simple flask API and deploy it on the VPS server?

The first project based on the GenAI knowledge I’ve gathered recently is going to be my own assistant that will help me with my various daily tasks. The foundation that will power that assistant is going to be my own API, which will enable the assistant to use not only AI models but also other services and tools that will extend its capabilities.

I’ve started with the simplest idea of an API running on the VPS server. I’ve decided to use Python Flask to implement it. In the first iteration, I’ve created a Flask API that was deployed on my VPS server with the use of a simple Nginx proxy and Gunicorn. Here are the steps that I followed to create and deploy the API.

1. New user

Connect to the VPS server using SSH and log in as user with sudo privileges. Create a new user for the new API service and grant it sudo privileges.

adduser api_user
usermod -aG sudo api_user

2. Setup python environment

Switch to the recently created user and navigate to its home directory. We will use a Python virtual environment to make it easier to control the Python version and manage our dependencies. First, we need to install the Python package manager pip and virtualenv. Then, check if virtualenv is installed correctly, verify its version, and create a new virtual environment. Finally, activate the virtual environment.

su api_user
cd /home/api_user
sudo apt-get install python3-pip
sudo pip3 install virtualenv
virtualenv --version
virtualenv assistant_api
source  assistant_api/bin/activate

3. Install dependencies

With virtual environment activated install necessary dependencies.

pip install wheel
pip install flask
pip install gunicorn

4. Prepare basic project file

After the initial configuration, we can create a basic Flask project file. Create a flask_api.py file with the following content. This is a simple Flask API that will return JSON with a message “hello world” when we send a GET request to the server. Additionally, there is a basic logging configuration that will log all requests and responses to the flask.log file.

import os
import logging
from flask import Flask, request, jsonify

app = Flask(__name__)

logging.basicConfig(filename='flask.log', level=logging.DEBUG)

app.logger.debug('Starting application')


@app.route('/', methods=['GET'])
def home():
    if (request.method == 'GET'):
        data = "hello world"
        app.logger.info('Request: %s', request)
        app.logger.info('Response: %s', data)
        return jsonify({'data': data})


if __name__ == '__main__':
    app.run(debug=True)

Now we can run the flask app with the following command to check if everything is working correctly. If there are no errors we can proceed to the next step.

python flask_api.py

We can also check if the logging is working correclty. There should be a flask.log file created in the same directory as flask_api.py file. The file should contain logs from the flask app. We can check it with the following command:

tail -f flask.log

5. Web Server Gateway Interface (WSGI) Configuration

WSGI enables communication between web servers and Python web apps or frameworks. The proxy server facilitates communication between applications and clients. Python web apps or frameworks cannot communicate with clients directly, so a WSGI server is required to enable communication between the proxy server and Python apps. In this configuration, we will use Gunicorn as a WSGI server. In this step, we create a wsgi.py file with the following content:

from flask_api import app
if __name__ == "__main__":
   app.run()

This will be in the same directory as the flask_api.py main module. The app object is an instance of the Flask class that we created in the flask_api.py file. To test it, we can start Gunicorn with the following command, where we specify the bind address and port where the app will be served.

gunicorn --bind 0.0.0.0:5000 wsgi:app

If there are no errors we can exit the process with CTRL+C, deactivate the virtual environment and proceed to the next step.

6. Create a service file for Gunicorn

The next step is to create a service file for Gunicorn. This will enable us to control the Gunicorn service with systemctl commands, and it will ensure that our API will be running even after the server is restarted. We will use the user that we created in the first step to run the service. Create a service file with the following command:

sudo vi /etc/systemd/system/flask_api.service

In the file we can put the following content:

[Unit]
Description=A Gunicorn configuration to serve Flask assitant API project
After=network.target
[Service]
User=api_user
Group=www-data
WorkingDirectory=/home/api_user
Environment="PATH=/home/api_user/assistant_api/bin"
ExecStart=/home/api_user/assistant_api/bin/gunicorn --workers 3 --bind unix:/home/api_user/flask_api.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target

In the file, update the paths to be correct for your setup. The working directory is the directory where the flask_api.py and wsgi.py files are located. ExecStart is the command that will be executed to start the service. Our service should be ready to be started. We can control it with the following commands:

sudo systemctl enable flask_api
sudo systemctl start flask_api
sudo systemctl status flask_api
sudo systemctl stop flask_api
sudo systemctl restart flask_api

7. Configure Nginx as a reverse proxy

Nginx is a web server that can also be used as a reverse proxy, load balancer, mail proxy, and HTTP cache. We will use it as a reverse proxy in that example. Install Nginx with the following command:

sudo apt-get install nginx

With the nginx installed we can create a configuration file. To make it work we need to create a configuration file in the sites-available directory and then create a symbolic link to it in the sites-enabled directory.

sudo vi /etc/nginx/sites-available/flask_api
server {
    listen ip:port_no;
    server_name domain_name;
location / {
        proxy_pass http://unix:/home/api_user/flask_api.sock;
    }
}

Set the ip and port_no according to your setup. The location in proxy_pass should be the same as we set previously in the service file.

In the next step we need to create a symbolic link to the configuration file in the sites-enabled directory.

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

When the configuration is ready, we can restart the Nginx server and check its status afterward. If there are no errors, the API should be up and running.

sudo systemctl restart nginx
sudo systemctl status nginx

The API should be up and running. If you want to make it accessible from the internet, you need to configure your firewall to allow connections to the port that you specified in the Nginx configuration. You can also use a domain name to make it easier to access the API. This depends on your setup and your specific needs, so I will not cover it in this tutorial.

Summary and credits

In this tutorial, we covered the essential steps to create and deploy a Flask API on a VPS server. Let’s recap the key points:

  • User Setup: We created a dedicated user with sudo privileges for running our API service.
  • Python Environment: Utilized a Python virtual environment to manage dependencies and control the Python version.
  • Flask Project: Developed a basic Flask API to demonstrate the deployment process.
  • WSGI Configuration: Integrated Gunicorn as the WSGI server to facilitate communication between the proxy server and our Flask app.
  • Systemd Service: Created a systemd service file for Gunicorn, ensuring the API runs reliably and persists across server reboots.
  • Nginx Configuration: Configured Nginx as a reverse proxy to forward requests to our Gunicorn server.

If you have any questions, do not hesitate to contact me. The tutorial was inspired by the following article: Deploying Flask Application on VPS Linux Server using Nginx

Next steps

In the next article I want to utilize docker and containerization to make it easier to deploy the API. I also want to add some basic form of authentication to provide some security to the API.