How to Deploy a Python App on VPS
Short answerTypical production flow on a VPSGrid Linux VPS: install Python, create a venv, install app dependencies, run the app behind Gunicorn (WSGI) or Uvicorn (ASGI), put Nginx in front, open ports 80/443, and add HTTPS. Framework-specific details vary — Django has a dedicated guide.
Overview
This is a general Python web app deployment pattern for unmanaged VPS hosting. You manage the runtime, process manager, web server, and TLS inside the guest OS. Install Python first if needed — How to Install Python and pip. Django projects: Deploy Django on VPSGrid.
Before you start
- SSH access to your VPS primary IP
- Domain DNS pointing to the VPS (for public HTTPS) — How to Point a Domain to a VPS
- Your app runs locally or in dev with known dependencies (
requirements.txtorpyproject.toml)
1) Prepare the app directory
sudo apt update
sudo apt install -y python3 python3-venv python3-pip nginx
sudo mkdir -p /var/www/myapp
sudo chown $USER:$USER /var/www/myapp
cd /var/www/myapp
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt # your pinned deps2) Choose an app server
| App type | Common server | Bind address |
|---|---|---|
| Flask, Django (WSGI) | Gunicorn | 127.0.0.1:8000 |
| FastAPI, Starlette (ASGI) | Uvicorn | 127.0.0.1:8000 |
Flask example (test):
pip install flask gunicorn
# app.py with Flask app object named `app`
gunicorn --bind 127.0.0.1:8000 app:appFastAPI example (test):
pip install fastapi uvicorn
uvicorn main:app --host 127.0.0.1 --port 80003) Run under systemd
Create /etc/systemd/system/myapp.service (adjust paths/command):
[Unit]
Description=My Python app
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
Environment="PATH=/var/www/myapp/venv/bin"
ExecStart=/var/www/myapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Restart=always
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp
curl -I http://127.0.0.1:8000/4) Put Nginx in front
Example server block for example.com:
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}sudo nginx -t
sudo systemctl reload nginx5) Firewall and HTTPS
- Allow ports 80 and 443 in ufw/iptables
- Add HTTPS with Let's Encrypt/Certbot or a purchased certificate — SSL Certificates Explained
- Set framework HTTPS settings (e.g. Django
SECURE_PROXY_SSL_HEADER, trusted hosts)
6) Production checklist
- Pin dependencies and test upgrades in staging
- Run database migrations (Django
migrate, Alembic, etc.) - Configure static/media files (especially Django)
- Set up log rotation and monitoring
- Keep OS and Python packages updated — How to Update Ubuntu on a VPS
Frequently Asked Questions
Should I expose Gunicorn/Uvicorn directly to the internet?
No. Bind to 127.0.0.1 and use Nginx (or another reverse proxy) on ports 80/443.
Is this guide only for Flask?
No. The same pattern applies to most Python web frameworks — swap the app server command and framework-specific settings.
502 errors after deploy?
Usually the upstream is down or Nginx points at the wrong port — Website Shows 502 or 504 Error.
Related Articles
Need More Help?
Open a ticket at billing.vpsgrid.net with your framework, systemd status, and Nginx error log excerpts. Guest app deployment is self-managed on unmanaged plans.
Was this answer helpful?