VPS Üzerinde Gunicorn ve Nginx ile Python Uygulamaları Nasıl Çalıştırılır
Ubuntu’da Flask veya FastAPI uygulamasını virtualenv, Gunicorn systemd hizmeti, Nginx ters proxy, TLS, ortam dosyaları, günlükleme ve 502 hataları ile worker boyutlandırma kontrol listesiyle dağıtın.

Yerleşik Flask sunucusu ve uvicorn --reload geliştirme içindir. Genel bir VPS’te worker’ları yeniden başlatan, localhost’a bağlanan ve TLS ile yavaş istemcileri işleyen bir ters proxy arkasında oturan bir süreç yöneticisi istersiniz. Gunicorn, Flask ve Django için olağan WSGI seçimidir. FastAPI, bir Uvicorn worker sınıfıyla Gunicorn altında çalışabilir. Nginx (veya Caddy) HTTPS’i sonlandırır ve 127.0.0.1:8000’e iletir.
Bu rehber yeniden başlatmaları atlatan bir düzeni adım adım geçer: proje /srv/app’de, virtualenv, sırlarla .env, bir gunicorn.service birimi, Nginx sunucu bloğu, Let's Encrypt ve gerçekten grep edebileceğiniz günlük dosyaları. Ayrıca worker sayıları, zaman aşımları ve neden 502 Bad Gateway’in neredeyse hiç ‘Nginx bozuk’ olmadığını konuşacağız — neredeyse her zaman Gunicorn çalışmıyor, yanlış sokete bağlı veya içe aktarmada çöküyor. Örnek Flask kullanır; komutun farklı olduğu yerlerde FastAPI notlarıyla.
Neden bu yığın
Gunicorn worker süreçlerini önceden çatallar. Farklı bir worker sınıfı kullanmadıkça her worker aynı anda bir isteği işler. Nginx yavaş istemcileri tamponlar ki worker’lar bir mobil ağa bayt gönderirken takılı kalmasın. systemd uygulama ölürse yeniden başlatır. Birlikte bu sıktır; gece 3’te istediğiniz şey budur.
- Gunicorn: kararlı WSGI/ASGI süreç modeli
- systemd: açılışta başlat, çöküşte yeniden başlat, journald günlükleri
- Nginx: TLS, statik dosyalar, istek boyutu limitleri, gzip
- venv: sistem Python’u temiz kalır
- localhost bağlama: uygulama Nginx dışında erişilemez
Gereksinimler
Ubuntu 22.04/24.04’te Python 3.10+ yeterlidir. pip’i root olarak sistem site-packages’e çalıştırmayın. TLS için bir alan adına ihtiyacınız vardır. Proxy olarak Caddy’yi tercih ederseniz bu makaledeki Gunicorn birimi aynı kalır — yalnızca ön uç yapılandırması değişir (Caddy makalesine bakın).
- Ubuntu 22.04 veya 24.04 VPS
- requirements.txt veya eşdeğeri olan uygulamanız
- Bir WSGI giriş noktası (Flask için: app:app) veya ASGI (FastAPI için: uvicorn worker’larıyla app:app)
- HTTPS için alan adı A kaydı
Adım 1: Sistem paketleri, kullanıcı ve proje dizini
Etkileşimli giriş yapamayan, koda sahip olan ve Gunicorn çalıştıran bir sistem kullanıcısı oluşturun. python3-venv ve derleme araçlarını kurmak, hâlâ C uzantıları derleyen paketlerde pip hatalarını önler.
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/Adım 2: Virtualenv ve bağımlılıklar
Dosya sahipliğinin doğru olması için venv’i appuser olarak oluşturun. Üretimde sürümleri sabitleyin. Kurulumdan sonra tek seferlik gunicorn --check-config veya bir Python içe aktarmasıyla uygulamayı içe aktarabildiğinizi doğrulayın. Buradaki içe aktarma hataları sonra 502 olan aynı hatalardır.
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')"Adım 3: Ortam dosyası
SECRET_KEY veya veritabanı URL’lerini systemd biriminde herkesin okuyabileceği git’e düşecek şekilde kodlamayın. EnvironmentFile kullanın. chmod 640, sahip root, grup appuser (veya tercih ederseniz sahip 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/.envAdım 4: Gunicorn systemd hizmeti
Nginx’i atlamak için bir nedeniniz yoksa 0.0.0.0 değil 127.0.0.1:8000’e bağlayın. Sync worker’lar için worker sayısı çoğu zaman (2 x CPU) + 1’dir; 2 vCPU VPS’te bu 5’tir, her worker ağır bir ML modeli yüklüyorsa fazla olabilir — o zaman 2–3 kullanın. FastAPI için --worker-class uvicorn.workers.UvicornWorker ayarlayın ve uvicorn kurun. Zaman aşımları en yavaş dürüst isteğinizi aşmalıdır; 2 dakikalık dışa aktarımlarınız varsa 30 saniye değil.
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 8000Adım 5: Nginx ters proxy ve TLS
Nginx 80/443’ü dinler ve Gunicorn’a proxy yapar. Yüklemeler için client_max_body_size önemlidir. proxy_read_timeout Gunicorn zaman aşımıyla eşleşmeli veya onu aşmalıdır. Sunucu bloğu HTTP’de çalıştıktan sonra Certbot ile sertifika alın (veya önü Caddy’ye çevirin). Aşağıdaki parça test edebilmeniz için yalnızca HTTP’dir; ardından dosyayı düzenleyebilen Certbot’u çalıştırın.
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.comAdım 6: Statik dosyalar, izinler ve Flask/FastAPI ayrıntıları
Yapabiliyorsanız statik dosyaları Nginx sunmalıdır; Gunicorn’dan daha hızlıdır. Django collectstatic, küçük uygulamalar için Flask send_from_directory veya sonra bir CDN. appuser ağacı okuyabilmelidir. FastAPI kullanıyorsanız venv’e uvicorn[standard] kurun ve ExecStart’ı UvicornWorker kullanacak şekilde değiştirin. TCP yerine Unix soketleri kullanıyorsanız proxy_pass’i sokete yöneltin ve www-data’nın yazabilmesi için izinleri eşleştirin.
# 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/appWorker’lar, bellek ve kesintisiz yeniden yüklemeler
Sync Gunicorn worker’ları basittir ve CPU’su hafif istek/yanıt API’leri için yeterlidir. Birçok eşzamanlı yavaş G/Ç beklemesi gerekiyorsa gevent veya bir ASGI worker düşünün — ölçün, tahmin etmeyin. Her worker uygulamanızı yükler; RAM ~= worker sayısı çarpı uygulama RSS. 300 MB’lık bir uygulamanın 8 worker’ıyla 2 GB VPS takas eder ve ‘rastgele yavaş’ hissedilir. systemctl reload gunicorn (HUP), dosyaları yerinde dağıttıysanız worker’ları yeni kodla yeniden başlatabilir; bağımlılıklar değişince tam yeniden başlatma daha nettir.
# 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/health502 ve sessiz çökmeleri giderme
502, Nginx’in yukarı akıştan geçerli bir yanıt alamadığı anlamına gelir. İlk komut journalctl -u gunicorn -e’dir. Yaygın nedenler: yanlış module:app adı, eksik .env anahtarı, Postgres çalışmıyor, 127.0.0.1’e bağlı ama Nginx başka bir ana makinede, SELinux (Ubuntu’da nadir) veya uygulamanın yalnızca IPv6 dinlemesi. 504 bir zaman aşımıdır. Uygulama X-Forwarded-Proto yok sayılırken HTTP’ye yönlendirince 301 döngüleri olur.
- systemctl status gunicorn — etkin mi?
- journalctl -u gunicorn -n 100 — ImportError, eksik env, veritabanı
- VPS’ten curl -v http://127.0.0.1:8000/ — bu başarısızsa Nginx masumdur
- nginx -t ve error.log — upstream prematurely closed connection
- ss -tulpn | grep 8000 — dinleyen yok
- Disk dolu — worker’lar gizemli şekillerde çöker
Güvenlik
Uygulama asla herkese açık bağlanmaz. Sırlar .env’de kalır. venv ve işletim sistemini yamalı tutun. Gunicorn’u root olarak çalıştırmayın. Giriş işliyorsanız oturum çerezlerini Secure ve SameSite yapın ve çerçeveyi X-Forwarded-Proto’ya yalnızca Nginx’ten güvenacak şekilde yapılandırın. Giriş yollarını Nginx’te veya uygulamada hız sınırlayın.
- 127.0.0.1 veya bir unix soketine bağlayın
- chmod 600 .env
- systemd’de root olmayan User=
- Certbot veya Caddy ile TLS
- Üretimde hata ayıklama kipini ve otomatik yeniden yüklemeyi kapatın
İpuçları
- Compose ve yük dengeleyiciler için DB bağlantısını kontrol eden bir /health yolu ekleyin
- Statik varlıkları Nginx’te bir cache-control başlığıyla gönderin
- E-postalar ve ağır işler için ayrı bir worker veya kuyruk kullanın (Redis + systemd)
- requirements.txt içinde gunicorn ve uvicorn sürümlerini sabitleyin
- İlk üretim geçişinden önce bir anlık görüntü alın
Bir VPS’teki Python uygulaması, Gunicorn altında bir systemd hizmeti olarak çalıştığında, yalnızca localhost’a bağlandığında ve TLS ile Nginx veya Caddy üzerinden ulaşıldığında üretime hazırdır. Sırları bir EnvironmentFile’a koyun, worker’ları bir blog yazısı formülünden değil RAM’e göre boyutlandırın ve 502’yi önce Gunicorn günlüğünden ayıklayın. Bu yol deponuz için belgelendikten sonra sonraki her dağıtım rsync veya git pull, pip install ve systemctl restart gunicorn’dur.