BlogEngineering

Engineering

How to deploy your Python app to a Virtual Private Server (VPS) using Github actions

Themba Mahlangu ยท 5 min read

In this short tutorial, we will be learning how to automate deployment to a VPS via SSH using Github actions and a simple Python script.

LEVEL - ๐Ÿ’ป ๐Ÿ’ป Intermediate - Advanced

Prerequisites

To get started you will need the following.

Linux VPS with Docker and Git installed - You can create a DigitalOcean droplet with Docker and git already installed here.

Dockerized Python or other application

Knowledge of Docker, Github actions and

SSH

Step 1 - Setting up your server for the first time

Our goal is
* to automatically deploy
* the
* latest
* version of an application to a server each time we
* push to a certain branch
* . To do that we need to first prepare our server for our application. You should have your server set up and ready to go.

1.1 Create SSH Keys and add them to Github

The workflow will run commands on a remote server via SSH. We need to create an SSH key and add it to the list of allowed keys on the server.

We prefer to generate fresh SSH keys for each of our servers. Generating the SSH key can be done on your local machine.

On your local machine, generate an SSH key

Code
 text

**

$ ssh-keygen -t ed25519 -C "your_email@example.com";

This creates a new SSH key, using the provided email as a label. Copy the private key to your Github secret variables. See this post on

how to add your private key as an environment variable

in Github.

Copy the private key to your clipboard and add it as an environment variable in Github.

Code
 text

$ pbcopy < ~/.ssh/id_ed25519

1.2 Add your public key to the server

  1. Copy the public key to the server using ssh-copy-id

    text

$ ssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote-host

  1. Manually copy the public key to the server

    text

`
>> copy keys to clipboard
$ pbcopy < ~/.ssh/id_ed25519.pub
or
$ pbcopy < ~/.ssh/id_rsa.pub

>> ssh into server and use text editor to paste contents in authorized_keys
$ nano ~/.ssh/authorized_keys
`

If you are not familiar with how to generate SSH keys we suggest you

read this post

from Github.

1.3 Clone repository to server and check your app is working.

*

Code
 This section is out of the scope of this article but has been included for completeness

* **
.

Clone the repository to the server and set up the app is working as expected. For example, if we were cloning Advantch’s sample fastapi project we would run the following.

Code
 text

**

$ mkdir demo $ git clone git@github.com:advantch/fastapi-starter-template.git ./demo $ cd fastapi-starter-template &amp;&amp; docker-compose build $ ufw allow 80 $ docker-compose up -d

mkdir core
- create a directory called demo to dump the contents of the repository into.

git clone git@github.com:advantch/fastapi-starter-template.git ./demo
- clone the repo into the folder and build the stack

ufw allow 80
- expose port 80 to the outside world

docker-compose up -d
- launch docker-compose in detached mode.

Step 2 - Automating deployment scripts

In this first part, we will add a script to automate deployment tasks using Invoke.

Invoke

is a Python task execution tool & library. This is a personal preference. You could also use a bash script in place of this.

Create a file called
tasks.py
in the root directory of your project.

Code
 text

`
from invoke import task

SERVER_APP_FOLDER = ‘/home/apps/demo’

@task
def deploy_app_to_server(c, docs=False, bytecode=False, extra=’‘):
“”“
Pulls latest branch, rebuilds containers and runs migration command
“”“
with c.cd(SERVER_APP_FOLDER):
c.run(“echo ‘Pulling latest commit and building containers’“)
c.run(“git pull”)
c.run(“docker-compose -f production.yml build”)

Code
    c.run("echo 'Containers successfully build launching app'")
    c.run("docker-compose -f production.yml run --rm django python manage.py migrate")
    c.run("docker-compose -f production.yml up -d")

`

SERVER_APP_FOLDER = '/home/apps/demo'
- this is the name of the app folder for the application. Rename this appropriately.

@task
decorator marks the function as a task that can be run by Invoke.

c.run("git pull")
- will pull the default branch from the server. This assumes you have already set up the server the first time as shown in step one.

Step 3 - Add a workflow to deploy your application to the server

Add the following workflow to
.github/workflows/deploy.yaml.

Code
 text

`
name: Deploy to demo app

on:
push:
branches:
- main_demo

jobs:
deploy-demo-app:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: fifsky/ssh-action@master
with:
host: ‘demo.advantch.com’
user: ‘apps’
key: ${{ secrets.PRIVATE_KEY }}
script: cd your-app-folder && python3 -m invoke deploy-demo-app-to-server
`

This workflow will run the job
deploy-demo-app
on every push to the main_demo branch.

host: 'demo.advantch.com'
- replace this with your host (IP or domain).

user: 'apps'
- set the SSH user name. e.g. ‘apps’

key
- defines the private key to use for login

script
- defines the script which will run on the server.

Step 4 - Test your workflow locally and deploy

To get fast feedback, we can use the excellent
[

* act
* **
](https://github.com/nektos/act)
library to test workflows locally before deploying them to GitHub.

Head

over to the GitHub page

and follow the instructions on how to install act.

Once that is done, test the workflow to make sure everything is working.

Run
act -l
to list available workflows.

Code
 text

`
$ act -l

>>
ID Stage Name
deploy-demo-app 0 deploy-demo-app
`

Test the workflow

Code
 text

`
$ export PRIVATE_KEY=$(cat ~/.ssh/id_ed25519)
$ act -j deploy-demo-app -s PRIVATE_KEY

>>

[Deploy to demo app/deploy-demo-app] ๐Ÿš€ Start image=catthehacker/ubuntu:act-latest
[Deploy to demo app/deploy-demo-app] ๐Ÿณ docker run image=catthehacker/…


| To delete this message of the day: rm -rf /etc/update-motd.d/99-one-click
[Deploy to demo app/deploy-demo-app] โœ… Success - Deploy to server
`

export PRIVATE_KEY=$(cat ~/.ssh/id_ed25519)
- creates an environment variable named
PRIVATE_KEY
and sets it to the private key generated earlier.

act -j deploy-demo-app -s PRIVATE_KEY
- run the job deploy-demo-app passing in the
PRIVATE_KEY
.

If everything went smoothly, you can commit and push to your remote repo on GitHub. The action will deploy the latest app to the server whenever you push to the main branch.

Conclusion

GitHub Actions in combination with automation scripts are powerful tools that you can use to automate all your software workflows and simplify your DevOps. We have included some additional reading materials below for areas we did not cover in detail.

Additional reading material


How to create a CI/CD pipeline with Github Actions


How to manage infrastructure with Terraform


Generating SSH keys for github


How to set up ubuntu on a server


Installing Docker on a server