Django projeniz geliştirme ortamında manage.py runserver ile sorunsuz çalışıyor olabilir, ancak Django belgeleri bu komutun canlı ortam için tasarlanmadığını açıkça söyler. Canlı ortamda isteği bir WSGI veya ASGI sunucusunun karşılaması, statik dosyaların web sunucusundan verilmesi ve uygulamanın yeniden başlatmalara dayanıklı bir servis olarak çalışması gerekir. Bu yazıda en yaygın kombinasyonu kuruyoruz: Gunicorn uygulama sunucusu, systemd soket ve servis birimleri, önde Nginx ve Let's Encrypt sertifikası.

Rehber temiz bir Ubuntu 24.04 LTS sunucuyu, SSH ile bağlanabildiğinizi ve elinizde Git deposunda duran bir Django projesi olduğunu varsayar. Örneklerde proje paketinin adı projem, alan adı ornek.com, sistem kullanıcısı myapp olarak geçiyor; kendi değerlerinizle değiştirin. Henüz Nginx ile reverse proxy mantığına yabancıysanız önce reverse proxy nedir yazımıza göz atmanızı öneririz.

Sürüm ve gereksinim notları

Sürüm ve gereksinim notları: Gunicorn, systemd soket, Nginx, Ortam dosyası

1 Kasım 2026 itibarıyla Django'nun güncel sürümü 6.1 serisidir (6.1.2). Uzun süreli destekli seri olan 5.2 LTS için genişletilmiş destek Nisan 2028'e kadar sürer. PyPI kayıtlarına göre Django 6.1 en az Python 3.12 ister; Ubuntu 24.04'ün varsayılan Python sürümü de 3.12 olduğu için ek bir Python kurulumuna gerek kalmaz. Gunicorn tarafında güncel seri 26.x'tir ve Python 3.10 veya üstünü destekler. Gunicorn belgeleri de taşındı: eski docs.gunicorn.org adresi artık gunicorn.org alan adına yönleniyor.

BileşenGöreviBu rehberdeki konumu
GunicornDjango'nun WSGI uygulamasını çalıştırır, worker süreçlerini yönetir/srv/myapp/venv/bin/gunicorn
systemd soketUNIX soketini açar, ilk istekte servisi başlatır/run/myapp.sock
NginxTLS, statik/medya dosyaları, yavaş istemcileri tamponlama/etc/nginx/sites-available/myapp
Ortam dosyasıSECRET_KEY ve veritabanı parolası gibi gizli değerler/etc/myapp/myapp.env

1. Paketler, kullanıcı ve dizin yapısı

Önce gerekli paketleri kurun ve uygulamayı root yerine kendi sistem kullanıcısıyla çalıştırmak için ayrı bir kullanıcı açın. Ubuntu 24.04'te python3 -m venv komutunun çalışması için python3-venv paketi gerekir.

bash
sudo apt update
sudo apt install -y python3-venv python3-dev build-essential nginx git
sudo useradd --system --create-home --home-dir /srv/myapp --shell /bin/bash myapp
sudo mkdir -p /etc/myapp

Dizin düzeni şöyle olacak: kod /srv/myapp/app, sanal ortam /srv/myapp/venv, toplanan statik dosyalar /srv/myapp/static, kullanıcı yüklemeleri /srv/myapp/media. Django belgeleri STATIC_ROOT ile MEDIA_ROOT değerlerinin farklı olmasını şart koşar; bu ikisini aynı klasöre koymayın.

bash
sudo -iu myapp
git clone https://github.com/KULLANICI/projem.git app
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r app/requirements.txt gunicorn
mkdir -p static media
exit

2. Gizli değerler ve canlı ortam ayarları

Django'nun dağıtım kontrol listesi SECRET_KEY değerinin büyük ve rastgele olmasını, kaynak koda girmemesini ve ortam değişkeninden ya da dosyadan okunmasını önerir. Rastgele bir anahtar üretip ortam dosyasına yazın:

bash
python3 -c "import secrets; print(secrets.token_urlsafe(50))"
sudo nano /etc/myapp/myapp.env
ini
DJANGO_SETTINGS_MODULE=projem.settings
DJANGO_SECRET_KEY=BURAYA_URETTIGINIZ_RASTGELE_DEGER
DB_PASSWORD=GUCLU_BIR_SIFRE
bash
sudo chown root:myapp /etc/myapp/myapp.env
sudo chmod 640 /etc/myapp/myapp.env

Ardından settings.py içinde canlı ortam değerlerini tanımlayın. Aşağıdaki satırlar kontrol listesindeki kritik ayarların karşılığıdır:

python
import os

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = False
ALLOWED_HOSTS = ["ornek.com", "www.ornek.com"]

STATIC_URL = "static/"
STATIC_ROOT = "/srv/myapp/static"
MEDIA_URL = "media/"
MEDIA_ROOT = "/srv/myapp/media"

# Yalnızca Nginx bu başlığı her istekte kendisi yazıyorsa güvenlidir
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
  • DEBUG: Belgeler canlıda asla açılmamasını söyler; hata sayfası kaynak kod parçalarını, yerel değişkenleri ve ayarları sızdırır.
  • ALLOWED_HOSTS: DEBUG = False iken bu liste uygun değilse Django hiç çalışmaz. Joker karakter kullanırsanız Host başlığını kendiniz doğrulamanız gerekir.
  • SECURE_PROXY_SSL_HEADER: Django belgeleri bu ayarı ancak uygulama bir proxy arkasındaysa, proxy gelen X-Forwarded-Proto başlığını siliyor ve yalnızca HTTPS isteklerde kendisi ekliyorsa açmanızı ister. Aşağıdaki Nginx yapılandırmasındaki proxy_set_header X-Forwarded-Proto $scheme; satırı istemcinin gönderdiği değeri her istekte ezer.
  • SESSION_COOKIE_SECURE / CSRF_COOKIE_SECURE: HTTPS kurulduktan sonra çerezlerin düz HTTP üzerinden gitmesini engeller.

Şimdi kontrolleri, veritabanı göçlerini ve statik dosya toplama adımını çalıştırın. check --deploy komutu, canlı ayarlarınızdaki eksikleri otomatik olarak listeler:

bash
sudo -iu myapp
cd app
set -a; . /etc/myapp/myapp.env; set +a
../venv/bin/python manage.py check --deploy
../venv/bin/python manage.py migrate
../venv/bin/python manage.py collectstatic --noinput
../venv/bin/python manage.py createsuperuser
exit

3. Gunicorn için systemd soket ve servis birimi

Gunicorn belgelerindeki systemd örneği iki birimden oluşur: soketi systemd açar, Gunicorn ilk bağlantıda başlar. Bu yapının avantajı, Nginx ile Gunicorn arasındaki UNIX soketinin izinlerini systemd'nin yönetmesi ve servis yeniden başlarken soketin açık kalmasıdır. Soket birimi:

ini
# /etc/systemd/system/myapp.socket
[Unit]
Description=myapp gunicorn soketi

[Socket]
ListenStream=/run/myapp.sock
SocketUser=www-data
SocketGroup=www-data
SocketMode=0660

[Install]
WantedBy=sockets.target

Servis birimi, belgelerdeki örneğin sanal ortam ve ortam dosyasıyla uyarlanmış hâlidir:

ini
# /etc/systemd/system/myapp.service
[Unit]
Description=myapp gunicorn
Requires=myapp.socket
After=network.target

[Service]
Type=notify
NotifyAccess=main
User=myapp
Group=myapp
WorkingDirectory=/srv/myapp/app
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/srv/myapp/venv/bin/gunicorn --workers 3 --access-logfile - projem.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.socket
sudo -u www-data curl --unix-socket /run/myapp.sock -H "Host: ornek.com" http://localhost/
systemctl status myapp.service

Worker sayısı: Gunicorn belgeleri başlangıç için (2 x CPU çekirdeği) + 1 formülünü verir ve ağır trafikte bile genellikle 4 ile 12 worker'ın yettiğini, fazlasının kaynak israfı olduğunu belirtir. Varsayılan değer 1'dir. Her worker ayrı bir süreç olduğu için RAM'i de hesaba katın; küçük bir VPS'te 2 ile 3 worker ile başlayıp yük altında ölçerek artırmak daha güvenlidir. Varsayılan istek zaman aşımı (--timeout) 30 saniyedir.

Servis birimlerinin ayrıntıları için systemd servis dosyası rehberi ve systemd servis yönetimi yazılarımıza bakabilirsiniz.

4. Nginx: statik, medya ve proxy

4. Nginx: statik, medya ve proxy: alias, usermod -aG myapp www-data, client_max_body_size, Medya dosyaları güvenilmezdir

Ubuntu'daki varsayılan siteyi kaldırın ve Django belgelerinin önerdiği gibi tanınmayan Host başlıklarına 444 dönen bir varsayılan sunucu bloğu ekleyin. Böylece sahte Host başlıklı istekler Django'ya hiç ulaşmaz ve loglarınızı doldurmaz.

nginx
# /etc/nginx/sites-available/myapp
upstream myapp_app {
    server unix:/run/myapp.sock fail_timeout=0;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    return 444;
}

server {
    listen 80;
    listen [::]:80;
    server_name ornek.com www.ornek.com;

    client_max_body_size 10m;

    location /static/ {
        alias /srv/myapp/static/;
    }

    location /media/ {
        alias /srv/myapp/media/;
    }

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
        proxy_pass http://myapp_app;
    }
}
bash
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
sudo usermod -aG myapp www-data
sudo nginx -t && sudo systemctl restart nginx
  • alias kullanırken location ve alias yollarının ikisini de / ile bitirin; nginx belgelerindeki örnek de bu şekildedir.
  • usermod -aG myapp www-data: Ubuntu'da yeni ev dizinleri genellikle diğer kullanıcılara kapalı oluşturulur. Nginx'in statik dosyaları okuyabilmesi için www-data kullanıcısını uygulama grubuna ekliyoruz; ev dizininin izinlerini herkese açmaktan daha dar bir yetkidir.
  • client_max_body_size: nginx varsayılanı 1m'dir; daha büyük dosya yüklemesi yapılan sitelerde 413 hatası alırsınız.
  • Medya dosyaları güvenilmezdir: Django belgeleri, kullanıcı yüklemelerinin web sunucusunca asla yorumlanmamasını ister. Medya location bloğuna PHP veya başka bir işleyici bağlamayın.

Gunicorn belgeleri, varsayılan senkron worker'lar kullanılırken yavaş istemcilerin önünde tamponlama yapan bir proxy bulunmasını şart koşar; aksi hâlde Gunicorn hizmet dışı bırakma saldırılarına açık kalır. Nginx'in varsayılan proxy tamponlaması bu işi görür. Server block yazımının ayrıntıları için Nginx server block rehberi yazımıza bakın.

5. HTTPS ve güvenlik duvarı

Alan adınızın A ve varsa AAAA kayıtları sunucuyu gösterdikten sonra Certbot'un Nginx eklentisiyle sertifikayı alın. Certbot server bloğunuza 443 dinleyicisini ve HTTP'den HTTPS'e yönlendirmeyi ekler. Kurulum ve otomatik yenileme ayrıntıları Let's Encrypt SSL kurulumu yazımızda.

bash
sudo certbot --nginx -d ornek.com -d www.ornek.com
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

6. Güncelleme adımları

Yeni bir sürüm yayınlarken kodu çekin, bağımlılıkları güncelleyin, göçleri ve collectstatic komutunu çalıştırın, ardından Gunicorn'a HUP sinyali gönderin. Gunicorn belgelerine göre HUP yapılandırmayı yeniden yükler, yeni worker'ları başlatır ve eskileri nazikçe durdurur; preload_app kullanmıyorsanız uygulama kodu da yeniden yüklenir.

bash
sudo -iu myapp
cd app && git pull
set -a; . /etc/myapp/myapp.env; set +a
../venv/bin/pip install -r requirements.txt
../venv/bin/python manage.py migrate
../venv/bin/python manage.py collectstatic --noinput
exit
sudo systemctl reload myapp.service

7. Loglar ve sık karşılaşılan hatalar

7. Loglar ve sık karşılaşılan hatalar: 502 Bad Gateway, 400 Bad Request, Statik dosyalar 404, Statik dosyalar 403

Gunicorn erişim ve hata loglarını standart çıktıya yazdığı için journald toplar; Nginx kendi log dosyalarını tutar:

bash
journalctl -u myapp.service -f
sudo tail -f /var/log/nginx/error.log
BelirtiMuhtemel nedenÇözüm
502 Bad GatewaySoket yok, servis çöküyor veya Nginx sokete bağlanamıyorsystemctl status myapp.socket myapp.service, journal'daki Python hatası, soket izinleri
400 Bad RequestAlan adı ALLOWED_HOSTS listesinde değilListeye ekleyin, servisi reload edin
Statik dosyalar 404collectstatic çalışmamış veya alias yolu yanlışSTATIC_ROOT ile alias yolunu karşılaştırın, sondaki eğik çizgiye bakın
Statik dosyalar 403www-data dizine erişemiyorGrup üyeliğini ve dizin izinlerini kontrol edin, Nginx'i yeniden başlatın
CSRF doğrulama hatası (403)HTTPS bilgisi uygulamaya ulaşmıyor veya istek başka bir kökenden geliyorX-Forwarded-Proto başlığı ve SECURE_PROXY_SSL_HEADER; farklı köken için CSRF_TRUSTED_ORIGINS
413 Request Entity Too LargeYükleme nginx sınırını aşıyorclient_max_body_size değerini artırın
Logda WORKER TIMEOUTİstek 30 saniyelik varsayılanı aşıyorYavaş sorguyu iyileştirin veya uzun işi arka plan kuyruğuna taşıyın; timeout'u körlemesine büyütmeyin

502 ve 504 hatalarının genel teşhisi için 502 ve 504 gateway hataları, log okuma için Linux loglarını okuma yazılarına bakabilirsiniz.

8. Yedekleme

Django tarafında yedeklemeniz gereken iki şey vardır: veritabanı ve MEDIA_ROOT altındaki kullanıcı yüklemeleri. Statik dosyalar collectstatic ile yeniden üretilebilir, kod da Git deposunda durur. Ortam dosyasını ise güvenli bir yerde ayrıca saklayın. Strateji için veritabanı yedekleme stratejileri, dosya yedeği için rsync ile sunucu yedekleme yazılarımızı okuyabilirsiniz.

Sık Sorulan Sorular

Gunicorn yerine ASGI sunucusu kullanmalı mıyım?

Projeniz senkron Django görünümlerinden oluşuyorsa WSGI ve Gunicorn yeterlidir. Async görünümler veya WebSocket gibi ihtiyaçlarınız varsa Django belgelerinin ASGI sunucuları bölümüne bakın; FastAPI tarafında kullandığımız Uvicorn kurulumunu FastAPI ve Uvicorn deploy yazımızda anlattık.

Statik dosyaları Django'ya sunduramaz mıyım?

Django belgeleri bunun topluluk paketleriyle mümkün olduğunu, ancak web sunucularının statik dosya sunmada çok daha hızlı olduğunu belirtir. Aynı sunucuda Nginx zaten varken statik ve medya dosyalarını Nginx'e bırakmak en basit yoldur.

Kısa süreliğine DEBUG = True açıp hatayı görebilir miyim?

Canlı sitede önerilmez. Hata ayrıntılarını journal'dan okuyun veya LOGGING ayarıyla bir dosyaya yazdırın. Belgeler ayrıca ADMINS ile 500 hatalarının e-postayla bildirilebileceğini, ancak bunun büyük ölçekte iyi ölçeklenmediğini söyler.

Docker ile kurmak daha mı iyi?

İkisi de geçerlidir. Tek sunucuda tek proje için systemd ve sanal ortam daha az hareketli parça içerir. Birden fazla servisi aynı yapıda yönetmek istiyorsanız Docker ile uygulama deploy yazımız yol gösterir.

Laravel veya Next.js projem de var, aynı sunucuda çalışır mı?

Her uygulama kendi server bloğu ve servisiyle aynı Nginx arkasında çalışabilir. Ayrıntılar için Laravel deploy ve Next.js deploy rehberlerimize bakın.

Yazılım ve hizmet adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen projeler veya şirketlerle ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: FastAPI ve Uvicorn deploy · GitHub Actions ile VPS deploy · Laravel deploy · Next.js deploy · Python nedir · Let's Encrypt kurulumu · VPS güvenlik sertleştirme