Како покретати Python апликације са Gunicorn и Nginx на VPS
Распоредите Flask или FastAPI апликацију на Ubuntu са virtualenv, systemd услугом Gunicorn, реверзним проксијем Nginx, TLS, датотекама окружења, логовањем и контролном листом за 502 грешке и димензионисање радника.

Уграђени Flask сервер и uvicorn --reload су за развој. На јавном VPS хоћете менаџер процеса који рестартује раднике, везује се на localhost и седи иза реверзног проксија који обрађује TLS и споре клијенте. Gunicorn је уобичајен WSGI избор за Flask и Django. FastAPI може да иде под Gunicorn са класом радника Uvicorn. Nginx (или Caddy) скида HTTPS и прослеђује на 127.0.0.1:8000.
Овај водич пролази кроз распоред који преживи рестарт: пројекат у /srv/app, virtualenv, .env са тајнама, јединица gunicorn.service, Nginx серверски блок, Let's Encrypt и лог датотеке које заиста можете да grep-ујете. Говорићемо и о броју радника, истецима времена и зашто 502 Bad Gateway скоро никад не значи „Nginx је покварен“ — скоро увек Gunicorn не ради, везан је за погрешан сокет или пада на увозу. Пример користи Flask, са напоменама за FastAPI тамо где се команда разликује.
Зашто овај стек
Gunicorn унапред покреће процесе радника. Сваки радник обрађује један захтев у датом тренутку осим ако користите другу класу радника. Nginx баферује споре клијенте да радници не заглављују на слању бајтова у мобилну мрежу. systemd рестартује апликацију ако умре. Заједно је то досадно — а управо то хоћете у три ујутру.
- Gunicorn: стабилан модел процеса WSGI/ASGI
- systemd: старт при покретању, рестарт при паду, journald логови
- Nginx: TLS, статичке датотеке, лимити величине захтева, gzip
- venv: системски Python остаје чист
- везивање на localhost: до апликације се стиже само кроз Nginx
Захтеви
Python 3.10+ на Ubuntu 22.04/24.04 је довољан. Не покрећите pip као root у системске site-packages. За TLS треба домен. Ако као прокси више волите Caddy, Gunicorn јединица из овог чланка остаје иста — мења се само предњи конфиг (види чланак о Caddy).
- Ubuntu 22.04 или 24.04 VPS
- Ваша апликација са requirements.txt или еквивалентом
- WSGI улазна тачка (за Flask: app:app) или ASGI (за FastAPI: app:app са uvicorn радницима)
- A запис домена за HTTPS
Корак 1. Системски пакети, корисник и директоријум пројекта
Направите системског корисника који не може интерактивно да се пријави, поседује код и покреће Gunicorn. Инсталација python3-venv и алата за градњу избегава падове pip на пакетима који још компајлирају C проширења.
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/Корак 2. Virtualenv и зависности
Правите venv као appuser да власништво датотека буде тачно. У продукцији закачите верзије. После инсталације потврдите да можете да увезете апликацију једнократним gunicorn --check-config или Python увозом. Грешке увоза овде су исте грешке које касније постану 502.
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')"Корак 3. Датотека окружења
Не ушивајте SECRET_KEY или URL базе у systemd јединицу тако да заврше у гиту читљивом свима. Користите EnvironmentFile. chmod 640, власник root, група appuser (или власник appuser ако више волите).
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/.envКорак 4. systemd услуга Gunicorn
Везујте се на 127.0.0.1:8000, не на 0.0.0.0, осим ако имате разлог да заобиђете Nginx. Број радника је често (2 × CPU) + 1 за sync раднике; на VPS са 2 vCPU то је 5, што може бити превише ако сваки радник учита тежак ML модел — тада 2–3. За FastAPI поставите --worker-class uvicorn.workers.UvicornWorker и инсталирајте uvicorn. Истеци треба да премаше најспорији поштен захтев, не 30 секунди ако имате извозе од 2 минута.
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 8000Корак 5. Реверзни прокси Nginx и TLS
Nginx слуша на 80/443 и проксира ка Gunicorn. client_max_body_size је важан за отпремања. proxy_read_timeout треба да одговара истеку Gunicorn или да га премаши. Када серверски блок ради на HTTP, издајте сертификат Certbot-ом (или пребаците предњи део на Caddy). Исечак испод је само HTTP да можете да тестирате; затим покрените Certbot који уме да уреди датотеку.
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.comКорак 6. Статичке датотеке, дозволе и специфичности Flask/FastAPI
Nginx треба да служи статику ако можете; бржи је од Gunicorn. Django collectstatic, Flask send_from_directory за сићушне апликације или касније CDN. appuser мора да може да чита стабло. Ако користите FastAPI, инсталирајте uvicorn[standard] у venv и промените ExecStart на UvicornWorker. Ако користите UNIX сокете уместо TCP, усмерите proxy_pass на сокет и ускладите дозволе да www-data може да пише на њега.
# 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/appРадници, меморија и преучитавање без прекида
Sync Gunicorn радници су једноставни и довољни за лаке API захтев/одговор. Ако треба много истовремених спорих I/O чекања, размотрите gevent или ASGI радника — мерите, не нагађајте. Сваки радник учитава апликацију; RAM ≈ број радника пута RSS апликације. VPS од 2 GB са 8 радника апликације од 300 MB ће отићи у swap и осећати се „насумично споро“. systemctl reload gunicorn (HUP) може да рестартује раднике са новим кодом ако сте распоредили датотеке на месту; пун рестарт је јаснији када се мењају зависности.
# 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/healthРешавање 502 и тихих падова
502 значи да Nginx није добио важећи одговор од upstream. Прва команда је journalctl -u gunicorn -e. Чести узроци: погрешно име module:app, недостаје кључ у .env, Postgres не ради, везан на 127.0.0.1 а Nginx на другом хосту, SELinux (ретко на Ubuntu) или апликација слуша само на IPv6. 504 је истек. Петље 301 се дешавају када апликација преусмерава на HTTP док се X-Forwarded-Proto игнорише.
- systemctl status gunicorn — да ли је active?
- journalctl -u gunicorn -n 100 — ImportError, недостаје env, база
- curl -v http://127.0.0.1:8000/ са VPS — ако ово не успе, Nginx је невин
- nginx -t и error.log — upstream prematurely closed connection
- ss -tulpn | grep 8000 — нико не слуша
- Диск пун — радници падају на мистериозне начине
Безбедност
Апликација се никад не везује јавно. Тајне остају у .env. Крпите venv и ОС. Не покрећите Gunicorn као root. Ако обрађујете пријаве, поставите колачиће сесије Secure и SameSite и подесите оквир да верује X-Forwarded-Proto само од Nginx. Ограничите брзину рута за пријаву у Nginx или у апликацији.
- везивање на 127.0.0.1 или unix сокет
- chmod 600 .env
- User= не-root у systemd
- TLS преко Certbot или Caddy
- Искључите debug режим и ауто-преучитавање у продукцији
Савети
- Додајте руту /health која проверава повезаност са базом за Compose и балансере оптерећења
- Шаљите статичке ресурсе са cache-control заглављем у Nginx
- Користите посебног радника или ред (Redis + systemd) за имејлове и тешке послове
- Закачите верзије gunicorn и uvicorn у requirements.txt
- Направите снимак пре првог продукционог пребацивања
Python апликација на VPS је спремна за продукцију када ради под Gunicorn као systemd услуга, везује се само на localhost и достижна је кроз Nginx или Caddy са TLS. Ставите тајне у EnvironmentFile, димензионишите раднике према RAM а не према формули са блога, и 502 прво отклањајте из Gunicorn журнала. Када је овај пут документован за ваш репозиторијум, свако касније распоређивање је rsync или git pull, pip install и systemctl restart gunicorn.