VPS'te Python Uygulaması Çalıştırma: venv, systemd ve Nginx Ters Vekil
Python ile yazdığınız web uygulamasını veya botu VPS'te kalıcı olarak çalıştırmak için adım adım rehber: sanal ortam (venv), Gunicorn, systemd servisi, Nginx ters vekil ve ücretsiz SSL.
Bilgisayarınızda python app.py ile sorunsuz çalışan bir uygulamayı sunucuya taşıdığınızda yeni sorular çıkar: Terminali kapatınca uygulama neden duruyor? Sunucu yeniden başlarsa ne olacak? Uygulamaya alan adıyla ve HTTPS üzerinden nasıl erişilir? Bu rehberde Ubuntu 24.04 çalışan bir VPS üzerinde, Python 3.12 ile yazılmış bir web uygulamasını üretim ortamına uygun biçimde yayına alacağız.
Kuracağımız yapı şöyle:
- Uygulama, kendine ait bir sanal ortamda (venv) çalışır.
- Gunicorn, uygulamayı yalnızca sunucunun içinden erişilebilen bir portta çalıştırır.
- systemd, Gunicorn'u bir servis olarak yönetir: açılışta başlatır, çökerse yeniden başlatır.
- Nginx, dışarıdan gelen istekleri karşılar ve uygulamaya iletir (ters vekil, reverse proxy).
Örnekte Flask kullanıyoruz. Django veya FastAPI için de adımlar neredeyse aynıdır; farkları yeri geldiğinde belirteceğiz.
1. Sunucuyu hazırlayın
SSH ile bağlanın, paketleri güncelleyin ve gerekli araçları kurun:
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-venv python3-pip nginx
python3 --version
Ubuntu 24.04'te python3 --version çıktısı Python 3.12.x olmalıdır. Uygulamayı root yerine kendine ait, yetkisi sınırlı bir kullanıcıyla çalıştırmak güvenlik açısından önemlidir:
sudo adduser --system --group --home /srv/myapp myapp
2. Uygulamayı yerleştirin ve sanal ortam oluşturun
Kodunuzu /srv/myapp/app klasörüne koyun. Git kullanıyorsanız:
sudo -u myapp git clone https://github.com/kullanici/proje.git /srv/myapp/app
Deneme için küçük bir Flask uygulaması da yazabilirsiniz. /srv/myapp/app/app.py:
from flask import Flask
app = Flask(__name__)
@app.get("/")
def index():
return "Merhaba, uygulama VPS'te çalışıyor!"
Şimdi sanal ortamı oluşturup bağımlılıkları kurun:
cd /srv/myapp
sudo -u myapp python3 -m venv venv
sudo -u myapp venv/bin/pip install --upgrade pip
sudo -u myapp venv/bin/pip install flask gunicorn
Projenizde requirements.txt varsa son satırın yerine sudo -u myapp venv/bin/pip install -r app/requirements.txt gunicorn kullanın.
Neden venv? Ubuntu 24.04, sistem Python'una
pipile paket kurmanızı engeller (externally-managed-environment hatası). Bu bilinçli bir korumadır: sistem araçlarının kullandığı paketleri bozmanızı önler. Her proje için ayrı bir venv, bağımlılık çakışmalarını da ortadan kaldırır.
3. Gunicorn ile deneyin
Flask'ın geliştirme sunucusu üretim için uygun değildir. Bunun yerine Gunicorn kullanacağız:
cd /srv/myapp/app
sudo -u myapp ../venv/bin/gunicorn --bind 127.0.0.1:8000 app:app
Başka bir terminalden curl http://127.0.0.1:8000 çalıştırdığınızda karşılama metnini görmelisiniz. app:app, "app.py dosyasındaki app nesnesi" demektir. Django'da bu genellikle proje.wsgi:application olur. FastAPI gibi ASGI uygulamalarında Gunicorn'a -k uvicorn.workers.UvicornWorker parametresi eklenir (bunun için uvicorn paketini de kurmanız gerekir).
Denemeden sonra Ctrl+C ile durdurun. Uygulamanın 127.0.0.1'e bağlandığına dikkat edin: dışarıdan doğrudan erişilemez, trafiği Nginx üzerinden alacak.
4. systemd servisi oluşturun
Terminal kapanınca uygulamanın durmaması ve sunucu yeniden başlayınca kendiliğinden açılması için bir systemd servisi yazıyoruz:
sudo nano /etc/systemd/system/myapp.service
İçeriği:
[Unit]
Description=myapp Gunicorn servisi
After=network.target
[Service]
User=myapp
Group=myapp
WorkingDirectory=/srv/myapp/app
Environment="PATH=/srv/myapp/venv/bin"
EnvironmentFile=-/srv/myapp/.env
ExecStart=/srv/myapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Birkaç açıklama:
EnvironmentFile, veritabanı şifresi veya API anahtarı gibi gizli bilgileri koddan ayrı tutmanızı sağlar. Başındaki-, dosya yoksa servisin yine de başlaması içindir.--workers 3küçük bir sunucu için makul bir başlangıçtır. Yaygın öneri (2 × çekirdek sayısı) + 1'dir, ama RAM'inizi de hesaba katın.Restart=on-failure, uygulama hata verip kapanırsa beş saniye sonra yeniden başlatır.
Servisi etkinleştirip başlatın:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
Durum satırında active (running) görmelisiniz. Hata varsa günlüklere bakın:
journalctl -u myapp -n 50 --no-pager
5. Nginx'i ters vekil olarak ayarlayın
Önce alan adınızın DNS A kaydını VPS'in IP adresine yönlendirin. Ardından bir Nginx site dosyası oluşturun:
sudo nano /etc/nginx/sites-available/myapp
server {
listen 80;
server_name ornek.com www.ornek.com;
client_max_body_size 20M;
location /static/ {
alias /srv/myapp/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;
}
}
/static/ bloğu CSS, JS ve görselleri Python'u yormadan doğrudan Nginx'in sunmasını sağlar. Uygulamanızda böyle bir klasör yoksa bu bloğu silebilirsiniz. Siteyi etkinleştirin:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
nginx -t komutu ayar dosyasında hata olup olmadığını kontrol eder; her değişiklikten sonra çalıştırmayı alışkanlık edinin.
6. Güvenlik duvarı ve HTTPS
UFW kullanıyorsanız SSH ve web trafiğine izin verin:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Let's Encrypt ile ücretsiz SSL sertifikası almak için:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d ornek.com -d www.ornek.com
Certbot, Nginx ayarınıza HTTPS bloğunu kendisi ekler ve sertifikanın otomatik yenilenmesini ayarlar. Yenilemenin çalıştığını sudo certbot renew --dry-run ile test edebilirsiniz.
Uygulamayı güncellemek
Kodda değişiklik yaptığınızda yapmanız gerekenler:
cd /srv/myapp/app
sudo -u myapp git pull
sudo -u myapp ../venv/bin/pip install -r requirements.txt
sudo systemctl restart myapp
Gizli bilgileri koddan ayırın
Veritabanı şifresi, API anahtarı veya SECRET_KEY gibi değerleri kodun içine yazmak, kod bir Git deposuna gönderildiğinde bunların sızmasına yol açabilir. systemd servisinde tanımladığımız /srv/myapp/.env dosyasını bunun için kullanın:
sudo nano /srv/myapp/.env
DATABASE_URL=postgresql://myapp:[email protected]:5432/myapp
SECRET_KEY=uzun-ve-rastgele-bir-deger
Dosyanın izinlerini yalnızca uygulama kullanıcısının okuyabileceği şekilde kısıtlayın ve servisi yeniden başlatın:
sudo chown myapp:myapp /srv/myapp/.env
sudo chmod 600 /srv/myapp/.env
sudo systemctl restart myapp
Python tarafında bu değerleri os.environ["DATABASE_URL"] ile okursunuz. Rastgele ve güçlü bir anahtar üretmek için python3 -c "import secrets; print(secrets.token_hex(32))" komutunu kullanabilirsiniz.
Sık yapılan hatalar
- Uygulamayı root ile çalıştırmak: Uygulamada bir açık bulunursa saldırgan tüm sunucuyu ele geçirir. Her zaman ayrı ve yetkisi sınırlı bir kullanıcı kullanın.
- Geliştirme sunucusunu dışarıya açmak:
flask run --host 0.0.0.0veya Django'nunrunserverkomutu üretim için tasarlanmamıştır. Gunicorn ve Nginx kullanın. - Hata ayıklama modunu açık bırakmak: Flask'ta
debug=True, Django'daDEBUG = Truehata sayfalarında kodunuzu ve ayarlarınızı gösterebilir. - Django'da
ALLOWED_HOSTSayarını unutmak: Alan adınızı bu listeye eklemezseniz Nginx üzerinden gelen istekler "Bad Request (400)" hatası alır. Statik dosyalar için decollectstatickomutunu çalıştırmayı unutmayın. - Klasör izinleri: Nginx'in statik dosyaları okuyabilmesi için
/srv/myapp/app/staticklasörünün ve üst klasörlerinwww-datakullanıcısı tarafından okunabilir olması gerekir. 403 hatası alıyorsanız ilk bakacağınız yer burasıdır.
Bot veya arka plan işi çalıştırıyorsanız
Telegram/Discord botu ya da web arayüzü olmayan bir betik için Nginx'e gerek yoktur. Aynı systemd dosyasında ExecStart satırını /srv/myapp/venv/bin/python bot.py yapmanız yeterlidir. systemd botu açık tutar ve çökerse yeniden başlatır.
Hangi sunucu yeterli?
Küçük bir Flask/FastAPI uygulaması veya bot için 1–2 vCPU ve 1–2 GB RAM'li bir VPS çoğu zaman yeterlidir. Makine öğrenmesi modeli, yoğun arka plan görevleri veya sürekli yüksek işlemci kullanımı varsa kaynakları başkalarıyla paylaşılmayan bir sunucu daha istikrarlı sonuç verir. TürkiyeVPS VPS ve VDS paketleri root erişimiyle gelir; bu rehberdeki her adımı kendi sunucunuzda uygulayabilirsiniz.
Sık Sorulan Sorular
screen veya tmux ile çalıştırmak yetmez mi?
Deneme için yeterlidir, ama sunucu yeniden başladığında ya da uygulama çöktüğünde kendiliğinden ayağa kalkmaz. systemd hem otomatik başlatma hem de günlük kaydı sağladığı için kalıcı uygulamalarda tercih edilmelidir.
"externally-managed-environment" hatası alıyorum, ne yapmalıyım?
Bu hata, sistem Python'una doğrudan pip install yapmaya çalıştığınızda görülür. Projeniz için python3 -m venv venv ile bir sanal ortam oluşturun ve paketleri venv/bin/pip ile kurun.
Uygulama 502 Bad Gateway hatası veriyor, neden?
Nginx'in arkasındaki uygulamaya ulaşamadığı anlamına gelir. systemctl status myapp ile servisin çalıştığını, journalctl -u myapp ile hata olup olmadığını ve Nginx'teki proxy_pass portunun Gunicorn'un portuyla aynı olduğunu kontrol edin.
Aynı VPS'te birden fazla Python uygulaması çalıştırabilir miyim?
Evet. Her uygulama için ayrı bir venv, farklı bir port (8000, 8001 gibi), ayrı bir systemd servisi ve ayrı bir Nginx server bloğu oluşturmanız yeterlidir.
Bunları da okuyun
512 MB veya 1 GB RAM'li bir VPS'te uygulamalarınız kapanıyorsa swap alanı ilk yardımdır. Ubuntu 24.04'te swap...
Bilgisayarınız kapanınca botunuz da susmasın. Python Telegram botunu Ubuntu 24.04 VPS'te systemd ile 7/24 çalı...
Metin2 sunucusu kuracaksınız ama VPS mi VDS mi alacağınızdan emin değil misiniz? Kaynak paylaşımı, RAM, disk,...