FastAPI Production Deployment Standards (VPS + Nginx)
A production-ready guide for deploying FastAPI applications on Ubuntu-based VPS/VPCs using Uvicorn, systemd services, PostgreSQL, and Nginx—with HTTPS, reverse proxying, and environment isolation.
🚀 Deploying a FastAPI Application on a VPS/VPC Using Nginx (Ubuntu)
This guide explains how to deploy a production-ready FastAPI application on an Ubuntu VPS/VPC using Nginx as a reverse proxy.
📌 Prerequisites
-
A VPS/VPC running Ubuntu
-
A Fast API application
-
Valid SSH authentication to the server (valid user)
-
Optional: Domain name pointing to server
Step-01: SSH to the Remote Server
Use the below SSH command to gain access to the remote server:
ssh -p {exposed_port_number} {user}@{server_ip / domain_name}
Step-02: Update & Upgrade Packages
Update & upgrade currently installed packages in the remote server using the following command:
sudo apt update && sudo apt upgrade -y
Step-03: Create Project Directory
Create proper directory for the project using below command:
mkdir /home/{user}/{project_name}/backend
Step-04: Upload or Clone Your Project
Option A: Clone from Git
Clone the project codebase from your Git repository (e.g. Gitea, Github etc.) to the directory using the following command:
git clone {Repository HTTPS URL}
Option B: Upload Using FileZilla (recommended for non-Git setups)
Using FileZilla, upload all the contents from local machine’s project directory.
/home/{user}/{project_name}/backend
Step-05: Install System Dependencies
Install required dependencies using the below command:
sudo apt install postgresql postgresql-contrib nginx curl make -y
Step-06: Configure PostgreSQL
To create the PostgreSQL database and user, you have to switch to psql interactive session by using the following command:
sudo -u postgres psql
Create Database:
You will be given a PostgreSQL prompt starting with postgres=#. Now to create the database, use the following command:
NB: Don't forget to put ';' (semicolon) after every statement!
CREATE DATABASE {database_name};
Create User:
Now create new user who to access the database using the following command:
CREATE USER {username} WITH PASSWORD '{password}';
Set Defaults:
Using the following commands, set default transaction isolation, character encoding and default timezone for the newly created user which are mandatory:
ALTER ROLE {username} SET client_encoding TO 'utf8';
ALTER ROLE {username} SET default_transaction_isolation TO 'read committed';
ALTER ROLE {username} SET timezone TO 'UTC';
Grant Privileges:
Now give the new user administrative access to the database so that the user can access the database using the command mentioned below:
GRANT ALL PRIVILEGES ON DATABASE {database_name} TO {username};
Exit:
Now you are done with the database part. Use the following command to exit the interactive terminal:
\q
Check the Project’s Environment File
Check the project environment file (.env) to ensure that all the database related variables are in order or not by using the following command:
sudo nano /{project_name}/backend/app/core/.env
Now check and update the following values:
DB_USER={username}
DB_PASSWORD={password}
DB_NAME={database_name}
Step-07: Virtual Environment Setup Using UV
Install UV:
To create the virtual environment (venv), we are going to use uv package. To install the package use the following command:
curl -LsSf https://astral.sh/uv/install.sh | sh
Verify Installation:
Now check if uv is installed properly or not using the following command:
uv --version
Go to Project Directory:
If everything is good till now, go to the project directory to create the virtual environment using the following command:
cd /{project_name}/backend
Create Virtual Environment:
By using the command mentioned below, create the virtual environment for the project:
uv venv
Activate Venv:
Activate the virtual environment using the following command:
source .venv/bin/activate
Install Dependencies:
Install all the project dependencies using the following command:
uv pip install -r requirements.txt
Verify Installed Dependencies:
Use the following command to check whether all the dependencies are installed correctly or not:
uv pip list
Step-08: Run Migrations
If Using Makefile:
Now you need to run the migrations to create all the related tables into the database. There should be a makefile inside the project root directory. If there is, then run the following commands to complete database migrations:
make migrations
make migrate
If Using Alembic Directly:
And if there is no makefile in the root directory, then run the following commands:
alembic revision --autogenerate -m "Auto migration"
alembic upgrade head
If there is no error, then your migration is successfully done!
Step-09: Create a Systemd Service
Now that you have completed the migration part, let’s jump into the deployment part. Firstly, you need to setup a service file for the Fast API project. Create the service file using the following command:
sudo nano /etc/systemd/system/{project_name}.service
Example Service File:
After you execute the command, an editor will appear. Now write the following lines into the service file based on your project context:
[Unit]
Description={a simple project description here}
After=network.target
[Service]
User={username}
Group={user_group}
WorkingDirectory={path_to_project_root} # (e.g. /home/{username}/{project_name}/backend)
EnvironmentFile={path_to_environment_file} # optional: if you have a .env file in the project (e.g. /home/{username}/{project_name}/backend/app/core/.env)
ExecStart={command_to_start_execution} # (e.g. /home/{username}/{project_name}/backend/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port {port_number})
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
Here, app.main:app means the Fast API’s app instance is located inside app directory’s main.py file.
Enable & start the Service:
After creating the service file, run the following commands to start the service:
sudo systemctl daemon-reload
sudo systemctl start {project_name}
sudo systemctl enable {project_name}
sudo systemctl status {project_name}
Step-10: Fix Directory Permissions (if needed)
You might find accessability issue while running migrations or starting the service. The problem is, the postgres user you are using has no execution right on the project directory. If you do not face and issues, just ignore this step. Otherwise run the following command to give access to the postgres user:
sudo chmod 755 /home/{username}/{project_name}/backend
Step-11: Set Up Nginx Reverse Proxy
Create a nginx configuration file using the following command:
sudo nano /etc/nginx/sites-available/{project_name}-backend
Example Config:
Now a text editor will appear in the console in which, the below config code has to be put in
server {
listen {port_number}; # for IPv4
listen [::]:{port_number}; # for IPv6
server_name {server_domain_or_ip};
client_max_body_size {size}; # add maximum request body size here (e.g. 3M)
location / {
proxy_pass http://127.0.0.1:{project_port_number}; # put the port number you mentioned in the {project_name}.service file
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Save & exit:
Ctrl + S, then Ctrl + X
Step-12: Enable the Nginx Configuration
Create symbolic link to the Nginx configuration file using the following command:
sudo ln -s /etc/nginx/sites-available/{project_name}-backend /etc/nginx/sites-enabled/
After that, check whether the nginx service is running properly or not using the following command:
sudo nginx -t
If there are any errors in the response, check it using the following command and try to solve the errors:
sudo tail -n 50 /var/log/nginx/error.log
Step-13: Restart Nginx
Now if everything is good till now, restart the Nginx server using the following command:
sudo systemctl reload nginx
sudo systemctl restart nginx
Step-14: Obtain SSL Certificate
This section upgrades your HTTP setup to secure HTTPS using free certificates from Let’s Encrypt. Firstly, install necessary packages via following command:
sudo apt install certbot python3-certbot-nginx -y
Then, obtain SSL Certificates using the following command:
sudo certbot --nginx -d {domain_name} -d www.{domain_name} # `-d www.{domain_name}` this part is optional
Choose:
- “Redirect
HTTPtoHTTPS”
Now Certbot will:
-
Generate SSL certificates
-
Modify your Nginx config
-
Reload Nginx
-
Add auto-renew
After running the command successfully, Certbot replaces your config with:
server {
listen 80;
listen [::]:80;
server_name {domain_name} www.{domain_name};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name {domain_name} www.{domain_name};
ssl_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{domain_name}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
client_max_body_size {size};
location / {
proxy_pass http://127.0.0.1:{project_port};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Now, using the following command, you can renew the SSL certificate every 60-90 days automatically:
sudo certbot renew --dry-run
🎉 Deployment Complete!
Your Fast API application is now successfully deployed and accessible via Nginx on your VPS.
Let's Build Something Scalable
We apply these same engineering principles to client projects. Ready to upgrade your stack?