Gunicorn is a production-grade WSGI server for running Python web applications (Flask, FastAPI, Django), and pairing it with Nginx as a reverse proxy is the standard deployment pattern for Python apps on a VPS.
Why Not Just Run the Development Server
Frameworks' built-in development servers aren't designed for production traffic — they typically handle one request at a time and lack the robustness needed for real usage. Gunicorn runs multiple worker processes, handling concurrent requests properly.
Prerequisites
- A working Python application (Flask, FastAPI, or similar)
- Nginx installed
Step 1 — Install Gunicorn
pip install gunicorn --break-system-packages
Step 2 — Test Gunicorn Directly
gunicorn myapp:app --bind 127.0.0.1:8000
For FastAPI specifically, use the Uvicorn worker class instead:
gunicorn myapp:app -k uvicorn.workers.UvicornWorker --bind 127.0.0.1:8000
Step 3 — Determine the Right Number of Workers
gunicorn myapp:app --workers 4 --bind 127.0.0.1:8000
A common starting formula is (2 x CPU cores) + 1, adjusted based on actual observed performance and memory constraints.
Step 4 — Create a systemd Service
sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Python Application
After=network.target
[Service]
User=deploy
WorkingDirectory=/var/www/myapp
ExecStart=/var/www/myapp/venv/bin/gunicorn myapp:app --workers 4 --bind 127.0.0.1:8000
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
Step 5 — Configure Nginx as a Reverse Proxy
server {
listen 80;
server_name yourdomain.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 ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 6 — Add HTTPS
sudo certbot --nginx -d yourdomain.com
Increasing Timeout for Long-Running Requests (Relevant for AI Workloads)
location / {
proxy_pass http://127.0.0.1:8000;
proxy_read_timeout 300s;
}
AI inference or other long-running operations may exceed Nginx's default proxy timeout, causing premature connection drops — increase this value to match your application's realistic response times.
Viewing Application Logs
sudo journalctl -u myapp -f
Deploying Updates
cd /var/www/myapp
git pull
sudo systemctl restart myapp
Common Errors
502 Bad Gateway — verify Gunicorn is actually running (sudo systemctl status myapp) and bound to the port Nginx expects.
Worker timeout errors in Gunicorn logs — increase Gunicorn's own --timeout flag if requests legitimately take longer than the default 30 seconds (common with AI inference workloads).
Continue Reading
- How to Serve a Machine Learning Model with FastAPI
- Nginx as a Reverse Proxy for Node.js/Docker Apps
- How to Install Let's Encrypt SSL with Certbot (Nginx & Apache)
Browse more articles in AI & Machine Learning on a VPS.