Post

From Code to Production - Deploying FastAPI to a VPS with GitHub Actions CI/CD

Learn how to deploy a FastAPI app to a VPS and set up GitHub Actions for automatic deployment every time you push code — a full CI/CD workflow from scratch.

From Code to Production - Deploying FastAPI to a VPS with GitHub Actions CI/CD

Prerequisites

  • A VPS (e.g., Ubuntu 22.04 on DigitalOcean, Linode, etc.)
  • Domain name (optional)
  • Basic knowledge of Python, Git, and Linux
  • SSH access to the server
  • GitHub account with FastAPI project repository
  1. Update your system
    1
    
    sudo apt update && sudo apt upgrade -y
    
  2. Install dependencies for building Python
    1
    2
    3
    4
    5
    6
    7
    
    sudo apt install -y software-properties-common \
     build-essential libssl-dev zlib1g-dev \
     libncurses5-dev libncursesw5-dev \
     libreadline-dev libsqlite3-dev \
     libgdbm-dev libdb5.3-dev libbz2-dev \
     libexpat1-dev liblzma-dev tk-dev \
     curl wget git
    
  3. Install Python (recommended: 3.10 or newer)
    1
    
    sudo apt install python3 python3-pip -y
    

    Check version

    1
    
    python3 --version
    
  4. Install FastAPI and Uvicorn
1
2
3
4
5
6
python3 -m venv venv
source venv/bin/activate

# Install FastAPI and Uvicorn:

pip install fastapi uvicorn
  1. Create a simple FastAPI app

Create a file named main.py

1
nano main.py

Paste this example:

1
2
3
4
5
6
7
8
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello from your VPS!"}

Run it:

1
uvicorn main:app --host 0.0.0.0 --port 8000

0.0.0.0 means “listen on all interfaces” so it’s accessible from outside.

Visit in browser:

1
http://your-vps-ip:8000

Summary

  • Install Python -> sudo apt install python3 python3-pip -y
  • Create venv -> python3 -m venv venv
  • Activate venv source -> venv/bin/activate
  • Install FastAPI + Uvicorn -> pip install fastapi uvicorn
  • Create app -> nano main.py
  • Run app -> uvicorn main:app –host 0.0.0.0 –port 8000

To create project directory:

1
2
mkdir ~/fastapi-app
cd ~/fastapi-app

To delete project directory:

1
2
cd ~
rm -rf fastapi-app
  • rm: remove command
  • -r: recursive (removes all subdirectories/files)
  • -f: force (no confirmation prompts)

Setup Deployment to VPS On Push With GitHub Actions

Set up SSH Key Access from GitHub to VPS

  • Generate SSH key on your local machine (in my case on Win11 CMD)
1
ssh-keygen -t ed25519 -C "github-actions-deploy"
  • Add public key to VPS - manually add the contents of github_fastapi_key.pub to:
    1
    
    ~/.ssh/authorized_keys
    
  • Add private key to GitHub

Go to GitHub repo → Settings → Secrets and variables → Actions → New repository secret:

Name: SSH_PRIVATE_KEY

Value: contents of github_fastapi_key NOT FROM PUB file but one without extension

VPS_HOST: your VPS IP or domain

VPS_USER: your VPS username

Set Up Your VPS to Accept Deploys

On the VPS:

  • Clone your repo manually the first time

This command will clone repo to the /home/ubuntu/fastapi-app folder

1
git clone git@github.com:yourusername/yourrepo.git /home/ubuntu/fastapi-app

If VPS can’t clone the repo then we need to generate SSH key directly on VPS and add it to the GitHub repot Public Key section.

  • Make sure your FastAPI app can run on the server
    1
    2
    3
    
    cd fastapi-app
    source venv/bin/activate
    uvicorn main:app --host 0.0.0.0 --port 8000
    

To deactivate virtual environment, run the command “deactivate”.

Run the command to keep uvicorn running after closing SSH session:

1
nohup uvicorn main:app --host 0.0.0.0 --port 8000 &> log.txt &

Create GitHub Actions Workflow

Create file: .github/workflows/deploy.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
name: Deploy to VPS

on:
  push:
    branches:
      - main  # or master, or whatever your deploy branch is

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: ⏳ Checkout code
      uses: actions/checkout@v3

    - name: 🔐 Set up SSH
      uses: webfactory/ssh-agent@v0.7.0
      with:
        ssh-private-key: $

    - name: 🚀 Deploy to VPS via SSH
      run: |
        ssh -o StrictHostKeyChecking=no $@$ << 'EOF'
          cd $
          git pull
          source venv/bin/activate
          pip install -r requirements.txt
          # enter correct cloned/application folder name
          sudo systemctl restart fastapi-app.service
        EOF

This keeps your app running and restarts it on reboot or deploy.

Create a systemd service on VPS:

1
sudo nano /etc/systemd/system/fastapi-app.service

Paste this (customize paths):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[Unit]
Description=FastAPI App
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/fastapi-app
ExecStart=/home/ubuntu/fastapi-app/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
Restart=always

[Install]
WantedBy=multi-user.target


Then enable and start it:

sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl enable fastapi-app
sudo systemctl start fastapi-app

Now, whenever we push to main (or the branch you chose), GitHub Actions will:

  • SSH into your VPS
  • Pull the latest code
  • Install dependencies
  • Restart your FastAPI app via systemd

If you’re getting “Access denied” when trying to manually add an SSH public key to your VPS as root, even though you’re logged in as root, run these commands on VPS as root:

1
2
3
4
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Then, manually add your public key to the file:

1
nano ~/.ssh/authorized_keys

Paste your public key (e.g., from github_fastapi_key.pub), then save and exit.

Generate requirements.txt automatically from your environment:

1
pip freeze > requirements.txt
This post is licensed under CC BY 4.0 by the author.