- Create Server & Initial Setup:
# Connect to server
ssh root@your_ip_address
# Update system
sudo apt update
sudo apt upgrade
# Install required packages (for pip)
sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx certbot python3-certbot-nginx supervisor
# For uv: Install the above packages (except python3-pip) and then install uv
sudo apt install python3-dev libpq-dev postgresql postgresql-contrib nginx certbot python3-certbot-nginx supervisor python3
curl -LsSf https://astral.sh/uv/install.sh | sh
- Configure PostgreSQL:
# Login to PostgreSQL
sudo -u postgres psql
# Create database and user
CREATE DATABASE project1;
CREATE USER project1user WITH PASSWORD 'project1password';
ALTER ROLE project1user SET client_encoding TO 'utf8';
ALTER ROLE project1user SET default_transaction_isolation TO 'read committed';
ALTER ROLE project1user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE project1 TO project1user;
- Setup Project Environment:
# Create project directory
mkdir /webapps
cd /webapps
mkdir project1
cd project1
# Create virtual environment
# For pip:
python3 -m venv env
# For uv:
uv venv env
# Create user group and project user
sudo groupadd --system webapps
sudo useradd --system --gid webapps --shell /bin/bash --home /webapps/project1 project1user
# Set correct permissions
sudo chown -R project1user:webapps .
- Project Setup:
# Clone your project from GitHub
git clone your_repository_url
# Create .env file
vi .env
# Add:
SECRET_KEY=your_secret_key
DB_PASSWORD=your_database_password
# Activate virtual environment
source env/bin/activate
# Install requirements
# For pip:
pip install -r requirements.txt
# For uv (choose one):
uv pip install -r requirements.txt
# OR
uv install -r requirements.txt
# Optional: If using uv with lock files
# Generate lock file locally before deployment:
uv pip compile requirements.txt -o requirements.lock
# Install from lock file on server:
uv pip sync requirements.lock
# Run migrations
python manage.py migrate --settings=project1.settings_prod
- Configure Gunicorn:
# Create Gunicorn start script
vi env/bin/gunicorn_start
# Add the script content:
#!/bin/bash
NAME="project1"
DIR=/webapps/project1/project1
SOCKFILE=/webapps/project1/run/gunicorn.sock
USER=project1user
GROUP=webapps
NUM_WORKERS=3
DJANGO_SETTINGS_MODULE=project1.settings_prod
DJANGO_WSGI_MODULE=project1.wsgi
timeout=120
cd $DIR
source ../env/bin/activate
# Add this line if using uv:
export UV_VIRTUALENV=1
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DIR:$PYTHONPATH
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
exec ../env/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--timeout $timeout \
--user=$USER \
--group=$GROUP \
--bind=unix:$SOCKFILE \
--log-level=debug
- Configure Supervisor:
# Create supervisor config
sudo vi /etc/supervisor/conf.d/project1.conf
# Add:
[program:project1]
command=/webapps/project1/env/bin/gunicorn_start
user=project1user
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/webapps/project1/logs/supervisor.log
# Create logs directory
mkdir logs
sudo chown project1user:webapps logs
# Reread and update supervisor
sudo supervisorctl reread
sudo supervisorctl update
- Configure Nginx:
# Remove default config
sudo rm /etc/nginx/sites-available/default
sudo rm /etc/nginx/sites-enabled/default
# Create new config
sudo vi /etc/nginx/sites-available/project1
# Add Nginx configuration:
upstream project1_app_server {
server unix:/webapps/project1/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name project1.example.com;
access_log /webapps/project1/logs/nginx-access.log;
error_log /webapps/project1/logs/nginx-error.log;
location /static/ {
alias /webapps/project1/project1/static/;
}
location /media/ {
alias /webapps/project1/project1/media/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://project1_app_server;
}
}
# Create symbolic link
sudo ln -s /etc/nginx/sites-available/project1 /etc/nginx/sites-enabled/project1
# Start Nginx
sudo service nginx start
- SSL Setup:
# Get SSL certificate
sudo certbot --nginx -d yourdomain.com
For deploying additional projects, repeat steps 2-8 with appropriate name changes (project2, project2user, etc.).
The main differences when using uv are:
- Installation of uv instead of pip
- Using
uv venvinstead ofpython3 -m venv - Different package installation commands with uv
- Addition of
export UV_VIRTUALENV=1in the Gunicorn script - Optional use of lock files for more reproducible deployments
All other steps remain the same regardless of whether you use pip or uv.