Hoe om Python-toepassings met Gunicorn en Nginx op 'n VPS te laat loop
Ontplooi 'n Flask- of FastAPI-toepassing op Ubuntu met 'n virtualenv, Gunicorn systemd-diens, Nginx reverse proxy, TLS, omgewingslêers, logboekhouding, en 'n kontrolelys vir 502-foute en werker-grootte.

Die ingeboude Flask-bediener en uvicorn --reload is vir ontwikkeling. Op 'n publieke VPS wil jy 'n prosesbestuurder hê wat werkers herbegin, aan localhost bind, en agter 'n reverse proxy sit wat TLS en stadige kliënte hanteer. Gunicorn is die gewone WSGI-keuse vir Flask en Django. FastAPI kan onder Gunicorn met 'n Uvicorn-werkerklas loop. Nginx (of Caddy) beëindig HTTPS en stuur aan na 127.0.0.1:8000.
Hierdie gids loop deur 'n uitleg wat herbeginne oorleef: projek in /srv/app, virtualenv, .env met geheime, 'n gunicorn.service-eenheid, Nginx-bedienerblok, Let's Encrypt, en loglêers wat jy werklik kan grep. Ons praat ook oor werker-tellings, tydverstreke, en hoekom 502 Bad Gateway byna nooit 'Nginx is stukkend' is nie — dit is byna altyd Gunicorn wat nie loop nie, aan die verkeerde soket gebind is, of by invoer ineenstort. Die voorbeeld gebruik Flask, met notas vir FastAPI waar die opdrag verskil.
Hoekom hierdie stapel
Gunicorn prefork werkersprosesse. Elke werker hanteer een versoek op 'n slag tensy jy 'n ander werkerklas gebruik. Nginx buffer stadige kliënte sodat werkers nie vassteek om grepe na 'n selfoonnetwerk te stuur nie. systemd herbegin die toepassing as dit sterf. Saam is dit vervelig, wat is wat jy om 03:00 wil hê.
- Gunicorn: stabiele WSGI/ASGI-prosesmodel
- systemd: begin by opstart, herbegin by ineenstorting, journald-logboeke
- Nginx: TLS, statiese lêers, versoekgrootte-limiete, gzip
- venv: stelsel-Python bly skoon
- localhost-binding: die toepassing is nie bereikbaar behalwe deur Nginx nie
Vereistes
Python 3.10+ op Ubuntu 22.04/24.04 is reg. Moenie pip as root in stelsel-site-packages laat loop nie. Jy het 'n domein vir TLS nodig. As jy Caddy as die proxy verkies, bly die Gunicorn-eenheid in hierdie artikel dieselfde — slegs die voorkant-konfig verander (sien die Caddy-artikel).
- Ubuntu 22.04- of 24.04-VPS
- Jou toepassing met 'n requirements.txt of ekwivalent
- 'n WSGI-ingangspunt (vir Flask: app:app) of ASGI (vir FastAPI: app:app met uvicorn-werkers)
- Domein A-rekord vir HTTPS
Stap 1: Stelselpakkette, gebruiker en projekgids
Skep 'n stelselgebruiker wat nie interaktief kan aanmeld nie, besit die kode, en laat Gunicorn loop. Die installering van python3-venv en bougereedskap vermy pip-mislukkings op pakkette wat steeds C-uitbreidings saamstel.
ssh root@YOUR_VPS_IP
apt update && apt -y upgrade
apt -y install python3 python3-venv python3-pip python3-dev build-essential nginx curl
adduser --system --group --home /srv/app appuser
mkdir -p /srv/app
chown appuser:appuser /srv/app
# Copy your code (example):
# rsync -a --delete ./myproject/ appuser@YOUR_VPS_IP:/srv/app/Stap 2: Virtualenv en afhanklikhede
Skep die venv as appuser sodat lêereienaarskap korrek is. Pin weergawes in produksie. Na installasie, bevestig jy die toepassing in 'n eenmalige gunicorn --check-config of 'n Python-invoer kan invoer. Invoerfoute hier is dieselfde foute wat later 502 word.
sudo -u appuser -H bash -lc '
cd /srv/app
python3 -m venv /srv/app/venv
/srv/app/venv/bin/pip install --upgrade pip
/srv/app/venv/bin/pip install -r /srv/app/requirements.txt gunicorn
'
# Flask example check:
sudo -u appuser -H /srv/app/venv/bin/python -c "from app import app; print('import ok')"Stap 3: Omgewingslêer
Moenie SECRET_KEY of databasis-URL's in die systemd-eenheid hardkodeer op 'n manier wat in wêreld-leesbare git beland nie. Gebruik EnvironmentFile. chmod 640, eienaar root, groep appuser (of eienaar appuser as jy verkies).
cat >/srv/app/.env <<'EOF'
FLASK_ENV=production
SECRET_KEY=replace-with-openssl-rand-hex-32
DATABASE_URL=postgresql://app:password@127.0.0.1:5432/app
EOF
chown appuser:appuser /srv/app/.env
chmod 600 /srv/app/.envStap 4: Gunicorn systemd-diens
Bind aan 127.0.0.1:8000, nie 0.0.0.0 nie, tensy jy 'n rede het om Nginx oor te slaan. Werker-telling is dikwels (2 x SVE) + 1 vir sinkroniese werkers; op 'n 2 vCPU-VPS is dit 5, wat te veel mag wees as elke werker 'n swaar ML-model laai — gebruik dan 2–3. Vir FastAPI, stel --worker-class uvicorn.workers.UvicornWorker en installeer uvicorn. Tydverstreke moet jou stadigste eerlike versoek oorskry, nie 30 sekondes as jy 2-minuut-uitvoere het nie.
cat >/etc/systemd/system/gunicorn.service <<'EOF'
[Unit]
Description=Gunicorn for the web app
After=network.target
[Service]
User=appuser
Group=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/srv/app/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 --timeout 60 --access-logfile - --error-logfile - app:app
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now gunicorn
systemctl status gunicorn --no-pager
ss -tulpn | grep 8000Stap 5: Nginx reverse proxy en TLS
Nginx luister op 80/443 en proxy na Gunicorn. client_max_body_size saak vir oplaai. proxy_read_timeout moet by Gunicorn se timeout pas of dit oorskry. Nadat die bedienerblok op HTTP werk, reik 'n sertifikaat met Certbot uit (of skakel die voorkant na Caddy). Die snit hieronder is slegs-HTTP sodat jy kan toets; laat dan Certbot loop wat die lêer kan redigeer.
cat >/etc/nginx/sites-available/app <<'EOF'
server {
listen 80;
server_name app.example.com;
client_max_body_size 20m;
location /static/ {
alias /srv/app/static/;
}
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;
proxy_read_timeout 90s;
}
}
EOF
ln -sf /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
nginx -t && systemctl reload nginx
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
apt -y install certbot python3-certbot-nginx
certbot --nginx -d app.example.comStap 6: Statiese lêers, toestemmings en Flask/FastAPI-besonderhede
Nginx moet statiese lêers dien as jy kan; dit is vinniger as Gunicorn. Django collectstatic, Flask send_from_directory vir piepklein toepassings, of later 'n CDN. Die appuser moet die boom kan lees. As jy FastAPI gebruik, installeer uvicorn[standard] in die venv en verander ExecStart om UvicornWorker te gebruik. As jy Unix-sokette in plaas van TCP gebruik, wys proxy_pass na die soket en pas toestemmings sodat www-data daarna kan skryf.
# FastAPI ExecStart example:
# ExecStart=/srv/app/venv/bin/gunicorn -k uvicorn.workers.UvicornWorker --workers 2 --bind 127.0.0.1:8000 app:app
# Unix socket variant:
# --bind unix:/run/gunicorn/app.sock
# Nginx: proxy_pass http://unix:/run/gunicorn/app.sock:
chown -R appuser:appuser /srv/appWerkers, geheue en nul-stilstand-herlaai
Sinkroniese Gunicorn-werkers is eenvoudig en genoeg vir SVE-ligtige versoek/respons-API's. As jy baie gelyktydige stadige I/O-wagte nodig het, oorweeg gevent of 'n ASGI-werker — meet, moenie raai nie. Elke werker laai jou toepassing; RAM ~= werkers maal toepassing-RSS. 'n 2 GB-VPS met 8 werkers van 'n 300 MB-toepassing sal swop en 'ewekansig stadig' voel. systemctl reload gunicorn (HUP) kan werkers met die nuwe kode herbegin as jy lêers in plek ontplooi het; 'n volle herbegin is duideliker wanneer afhanklikhede verander.
# After git pull / rsync of new code:
sudo -u appuser -H /srv/app/venv/bin/pip install -r /srv/app/requirements.txt
systemctl restart gunicorn
curl -I https://app.example.com/healthFoutopsporing van 502 en stille ineenstortings
502 beteken Nginx kon nie 'n geldige antwoord van upstream kry nie. journalctl -u gunicorn -e is die eerste opdrag. Gewone oorsake: verkeerde module:app-naam, ontbrekende .env-sleutel, Postgres loop nie, gebind aan 127.0.0.1 maar Nginx op 'n ander gasheer, SELinux (skaars op Ubuntu), of die toepassing luister slegs op IPv6. 504 is 'n tydverstreke. 301-lusse gebeur wanneer die toepassing na HTTP herlei terwyl X-Forwarded-Proto geïgnoreer word.
- systemctl status gunicorn — is dit aktief?
- journalctl -u gunicorn -n 100 — ImportError, ontbrekende env, databasis
- curl -v http://127.0.0.1:8000/ vanaf die VPS — as dit misluk, is Nginx onskuldig
- nginx -t en error.log — upstream het verbinding voortydig gesluit
- ss -tulpn | grep 8000 — niks luister nie
- Skyf vol — werkers stort op geheimsinnige maniere ineen
Sekuriteit
Die toepassing bind nooit publiek nie. Geheime bly in .env. Hou die venv en OS gekol. Moenie Gunicorn as root laat loop nie. As jy aanmeldings hanteer, stel sessiekoekies Secure en SameSite, en konfigureer die raamwerk om X-Forwarded-Proto slegs vanaf Nginx te vertrou. Tempo-beperk aanmeldroetes in Nginx of in die toepassing.
- bind 127.0.0.1 of 'n unix-soket
- chmod 600 .env
- Nie-root User= in systemd
- TLS via Certbot of Caddy
- Deaktiveer ontfoutmodus en outo-herlaai in produksie
Wenke
- Voeg 'n /health-roete by wat DB-konnektiwiteit vir Compose en lasbalanseerders nagaan
- Verskeep statiese bates met 'n cache-control-kopskrif in Nginx
- Gebruik 'n aparte werker of tou (Redis + systemd) vir e-posse en swaar take
- Pin gunicorn- en uvicorn-weergawes in requirements.txt
- Neem 'n momentopname voor die eerste produksie-oorskakeling
'n Python-toepassing op 'n VPS is produksiegereed wanneer dit onder Gunicorn as 'n systemd-diens loop, slegs aan localhost bind, en deur Nginx of Caddy met TLS bereik word. Sit geheime in 'n EnvironmentFile, grootte werkers volgens RAM eerder as 'n blogpos-formule, en ontfout 502 eers vanuit die Gunicorn-joernaal. Sodra hierdie pad vir jou repo gedokumenteer is, is elke latere ontplooiing rsync of git pull, pip install, en systemctl restart gunicorn.