چھوٹے VPS پر Django یا Flask: کون سا بہتر ہے؟
1 سے 2 GB VPS پر Django اور Flask کی اصل لاگت جانیں: ہر gunicorn worker کتنی RAM لیتا ہے، اور چھوٹا server حقیقتاً کتنے workers چلا سکتا ہے۔
چھوٹے VPS پر Django اور Flask کی لاگت
چھوٹے VPS پر Django اور Flask کا موازنہ پہلے memory کا سوال ہے۔ Django اپنے ہر worker process میں object relational mapper (ORM)، migration machinery اور، اگر آپ اسے enable کریں، admin site load کرتا ہے۔ Flask ایک router اور request object load کرتا ہے۔ 1 GB کے server پر یہ فرق طے کرتا ہے کہ کتنے workers چل سکتے ہیں، اور worker count طے کرتا ہے کہ آپ بیک وقت کتنی requests serve کر سکتے ہیں۔
یہ لاگت صرف اسی صورت میں Django کے لیے مسئلہ بنتی ہے جب آپ اس کی فراہم کردہ functionality دوبارہ نہ بنائیں۔ User accounts، sessions اور admin panel والی app کے لیے Django موزوں ہے: ہر worker کی اضافی RAM اس code کی قیمت ہے جو آپ کو خود نہیں لکھنا پڑتا۔ ایسے datastore کے سامنے JSON API کے لیے، جسے آپ پہلے ہی چلا رہے ہوں، Flask موزوں ہے، کیونکہ اس کی کوئی اضافی built-in functionality load نہیں ہوگی۔ یہ انتخاب app کی ضروریات کے مطابق ہوتا ہے۔ نیچے دی گئی measurements بتاتی ہیں کہ آپ کی app کس طرف آتی ہے۔
ایک gunicorn worker کتنی memory استعمال کرتا ہے؟
The data behind this chart
[
{
"label": "Bare Python 3.12 process",
"rss_mb": 14,
"pss_mb": 9
},
{
"label": "Flask, one route",
"rss_mb": 42,
"pss_mb": 26
},
{
"label": "Flask + SQLAlchemy",
"rss_mb": 58,
"pss_mb": 38
},
{
"label": "Django, admin disabled",
"rss_mb": 78,
"pss_mb": 47
},
{
"label": "Django, admin enabled",
"rss_mb": 96,
"pss_mb": 58
}
]یہ اعداد و شمار Ubuntu 24.04 پر Python 3.12، gunicorn کے تین workers اور فعال preload کے ساتھ ہر طرز کی hello world ایپ کے لیے عام طور پر شائع کیے گئے تخمینی اعداد ہیں۔ انہیں کم از کم بنیاد سمجھیں، کیونکہ آپ کے اپنے imports اس کے علاوہ memory استعمال کریں گے۔ Admin فعال ہونے والی Django worker 96 MB resident memory استعمال کرتی ہے، جبکہ اس میں memory کا متناسب حصہ 58 MB ہے۔ ان دونوں اعداد کے درمیان فرق اگلے section کا موضوع ہے۔
اپنے server پر یہی measurement بنائیں۔
sudo apt update && sudo apt install -y python3-venv
python3 -m venv /srv/site1/.venv
/srv/site1/.venv/bin/pip install django gunicorn setproctitlesetproctitle install کریں۔ اس کے موجود ہونے پر gunicorn اپنے processes کے نام بدل کر gunicorn: master [site1] اور gunicorn: worker [site1] کر دیتا ہے۔ اسی وجہ سے اگلی commands workers کو اندازے سے نہیں بلکہ نام کے ذریعے تلاش کر سکتی ہیں۔
pgrep -af gunicorn
ps -o pid,rss,args -p $(pgrep -d, -f 'gunicorn: worker')rss column resident set size کو kilobytes میں دکھاتا ہے: یعنی memory کا ہر وہ page جسے process اس وقت RAM میں رکھتا ہے۔ تمام workers کے اعداد جمع کرنے سے نتیجہ بہت زیادہ آتا ہے، کیونکہ fork کیا ہوا worker اپنے parent اور دوسرے workers کے ساتھ pages share کرتا ہے۔ اس لیے وہی page کئی بار شمار ہوتا ہے۔ اس کے بجائے kernel سے proportional set size (PSS) لیں۔ PSS ہر shared page کو ان processes کے درمیان تقسیم کرتا ہے جو اسے map کرتے ہیں۔
for pid in $(pgrep -f 'gunicorn: worker'); do awk -v p="$pid" '/^Pss:/ {printf "%s %d MB\n", p, $2/1024}' /proc/$pid/smaps_rollup; doneاسے اس user کے طور پر چلائیں جو workers کا مالک ہے، یا sudo استعمال کریں۔ Budget بنانے کے لیے PSS column استعمال کریں، کیونکہ PSS درست طور پر memory جمع کرتا ہے، جبکہ RSS نہیں کرتا۔
Django اس وجہ سے بڑا ہے کہ django.setup() کیا کرتا ہے۔ یہ INSTALLED_APPS کی ہر entry import کرتا ہے، application registry بناتا ہے، اور ہر model class کے ساتھ اس کے ہر field کے لیے Python object بھی بناتا ہے۔ django.contrib.admin شامل کرنے سے admin autodiscovery چلتا ہے۔ یہ ہر app کا admin module import کرتا ہے اور forms اور template layers کو بھی شامل کر دیتا ہے۔ Flask worker Werkzeug اور Jinja2 import کرتا ہے، پھر رک جاتا ہے۔
ایک اہم وضاحت: اکثر framework مجموعی memory کا چھوٹا حصہ ہوتا ہے۔ جو worker cloud SDK یا کسی numeric library کو import کرتا ہے، وہ Django سے زیادہ memory اسی library کی وجہ سے استعمال کر سکتا ہے۔ Framework کو مسئلہ قرار دینے سے پہلے اپنی حقیقی app کی measurement کریں۔
Copy on write اور preload سے تعداد کیوں بدلتی ہے
Gunicorn کا master process workers کو fork کرتا ہے۔ fork() کے فوراً بعد child، parent کے ساتھ ہر memory page share کرتا ہے، اور kernel کسی page کو صرف اس وقت copy کرتا ہے جب دونوں میں سے کوئی ایک اس میں لکھتا ہے۔ اس لیے سرور پر Django کا model registry ایک بار موجود ہے یا چار بار، اس کا انحصار اس بات پر ہے کہ اسے fork سے پہلے بنایا گیا تھا یا بعد میں۔
preload_app بند ہونے پر ہر worker fork ہونے کے بعد آپ کی application import کرتا ہے، اس لیے ہر worker اپنی الگ private copy بناتا ہے۔ اسے فعال کرنے پر master application کو ایک بار import کرتا ہے اور workers وہ pages inherit کرتے ہیں۔
import gc
bind = "unix:/run/site1/gunicorn.sock"
umask = 0o007
workers = 3
timeout = 30
preload_app = True
max_requests = 500
max_requests_jitter = 50
def when_ready(server):
gc.freeze()CPython copy on write کے خلاف کام کرتا ہے۔ ہر object header میں reference count ہوتا ہے، اور object کو چھونے سے اس header میں لکھا جاتا ہے۔ اس طرح garbage collector کے heap میں آگے بڑھنے کے ساتھ shared pages ایک ایک کر کے copy ہو جاتے ہیں۔ gc.freeze() اب تک allocate کی گئی تمام memory کو ایک permanent generation میں منتقل کرتا ہے، جہاں collector دوبارہ نہیں جاتا۔ اس سے ان pages کا بڑا حصہ shared رہتا ہے۔ when_ready درست hook ہے، کیونکہ یہ preload کے بعد اور پہلے worker کے fork ہونے سے پہلے چلتا ہے۔ اسے شامل کرنے سے پہلے اور بعد میں PSS ناپیں، کیونکہ بچت اس بات پر منحصر ہے کہ import time state میں آپ کی application کا کتنا حصہ موجود ہے۔
Preload کی ایک لاگت deploy کے دن بہت سے لوگوں کو حیران کرتی ہے۔ systemctl reload، HUP بھیجتا ہے، اور HUP پر gunicorn کا documented behavior یہ ہے کہ وہ اپنی configuration دوبارہ load کرتا ہے اور نئے workers شروع کرتا ہے۔ جب application preloaded ہو تو یہ آپ کے code کو دوبارہ import نہیں کرتا، اس لیے worker processes نئے ہونے کے باوجود نئی release نہیں چل رہی ہوتی۔ Code تبدیل کرنے کے بعد systemctl restart استعمال کریں، یا اگر پرانے workers کو پہلے requests مکمل کرنے کا وقت دینا ہو تو USR2 پھر WINCH sequence استعمال کریں۔
1 GB VPS ایمانداری سے کتنے workers چلا سکتا ہے؟
The data behind this chart
[
{
"label": "Ubuntu 24.04 base",
"ram_mb": 190
},
{
"label": "nginx",
"ram_mb": 12
},
{
"label": "PostgreSQL, default config",
"ram_mb": 120
},
{
"label": "Headroom you must leave",
"ram_mb": 150
},
{
"label": "Left for gunicorn workers",
"ram_mb": 550
}
]یہ اس سرور پر idle حالت کے اعداد ہیں جو کوئی سروس فراہم نہیں کر رہا۔ تقریباً 550 MB application workers کے لیے باقی رہتی ہے، اور اس میں پہلی request کی memory بھی شامل نہیں۔
اب تقسیم کریں، اور محتاط انداز سے تقسیم کریں۔ Request چلتے وقت memory استعمال کرتی ہے: ایک queryset چند ہزار rows لوڈ کرتا ہے، پھر template render ہوتا ہے۔ فی worker peak memory عموماً idle figure سے تقریباً دو گنا ہوتی ہے، اس لیے budget بھی دو گنا رکھیں۔ Django میں admin کے ساتھ idle memory 58 MB ہو تو یہ box چار workers چلا سکتا ہے۔ Flask میں SQLAlchemy کے ساتھ 38 MB ہو تو یہ سات workers چلا سکتا ہے۔
Gunicorn کی (2 x cores) + 1 تجویز یہ فرض کرتی ہے کہ CPU محدود resource ہے اور RAM محدود نہیں۔ چھوٹے VPS پر صورت حال اس کے برعکس ہوتی ہے۔ Host مصروف ہونے پر ایک shared vCPU بھی ایک مکمل core کی work capacity سے کم فراہم کرتا ہے۔ اس بات کو سمجھنا ضروری ہے، ورنہ آپ اپنے code کو بلاوجہ ذمہ دار ٹھہرا سکتے ہیں: شور پیدا کرنے والے پڑوسی سے CPU steal time top میں st figure کے طور پر ظاہر ہوتا ہے۔
اگر آپ کی views زیادہ تر database یا upstream API کا انتظار کرتی ہیں تو یہاں processes کے مقابلے میں threads بہتر ہیں۔ --worker-class gthread --workers 2 --threads 4 دو workers کی memory cost پر آٹھ concurrent requests فراہم کرتا ہے، کیونکہ threads interpreter اور framework کی ایک ہی loaded copy share کرتے ہیں۔ global interpreter lock کا مطلب ہے کہ CPU استعمال کرنے والی view کو threads سے فائدہ نہیں ہوتا۔
اس box پر swap فعال کریں۔ swap کے بغیر 1 GB VPS میں memory spike process کو kill کر دیتی ہے، جبکہ swapfile اسی spike کو ایک سست request میں تبدیل کر دیتی ہے۔
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --systemاس کے بعد application پر خود حد مقرر کریں۔ gunicorn unit میں MemoryMax=600M رکھنے سے kernel پوری machine میں کسی process کو منتخب کرنے کے بجائے آپ کی app کے cgroup سے memory واپس لیتا ہے۔ اس طرح runaway request آپ کا SSH session ختم نہیں کرتی۔
سرد آغاز اور restart کا رویہ
The data behind this chart
[
{
"label": "Flask, one route",
"cold_start_ms": 90
},
{
"label": "Flask + SQLAlchemy",
"cold_start_ms": 260
},
{
"label": "Django, admin disabled",
"cold_start_ms": 480
},
{
"label": "Django, admin enabled",
"cold_start_ms": 720
}
]Boot cost دو مرتبہ ادا ہوتی ہے: ہر deploy پر، اور crash کے بعد ہر automatic restart پر۔ ایک minimal Flask ایپ تقریباً 90 ms میں تیار ہو جاتی ہے، جبکہ admin enabled ہونے کے ساتھ Django کو اسی shared vCPU پر تقریباً 720 ms درکار ہوتے ہیں۔ یہ دونوں عام طور پر شائع کیے جانے والے اعداد و شمار ہیں۔ اپنی ایپ کی پیمائش کریں، کیونکہ اصل وقت کا زیادہ انحصار dependencies پر ہوتا ہے۔
cd /srv/site1
DJANGO_SETTINGS_MODULE=site1.settings /srv/site1/.venv/bin/python -X importtime -c "import django; django.setup()" 2>&1 | tail -20آخری سطور cumulative microseconds کے لحاظ سے سب سے سست imports دکھاتی ہیں۔ Flask کے لیے اپنے module پر یہی flag چلائیں: python -X importtime -c "import app"۔
preload فعال ہونے پر master یہ cost ایک مرتبہ ادا کرتا ہے اور ہر forked worker فوراً start ہو جاتا ہے۔ preload غیر فعال ہونے پر ہر worker یہ cost ادا کرتا ہے، اور gunicorn کا timeout boot کے ساتھ ساتھ request کو بھی شامل کرتا ہے۔ جو worker timeout seconds کے اندر check in نہیں کرتا، اسے ختم کر کے نیا worker شروع کر دیا جاتا ہے۔ اس لیے slow shared vCPU پر heavy ایپ restart loop میں پھنس سکتی ہے اور کبھی کوئی request serve نہیں کرتی۔ log میں یہ درج ہوتا ہے:
[2026-08-09 09:14:02 +0000] [981] [CRITICAL] WORKER TIMEOUT (pid:1004)Migrations کو unit میں رکھیں، application startup code میں نہیں۔ ExecStartPre کسی بھی worker کے وجود میں آنے سے پہلے ایک مرتبہ چلتا ہے۔ migrate کو اپنی ایپ کے اندر رکھنے سے تین workers ایک ہی schema lock کے لیے بیک وقت مقابلہ کرتے ہیں۔
تعیناتی کی ساخت تقریباً ایک جیسی ہے
Process manager
دونوں frameworks gunicorn کے تحت چلتے ہیں، اور gunicorn systemd کے تحت چلتا ہے۔
[Unit]
Description=gunicorn for site1
After=network.target
[Service]
User=site1
Group=www-data
WorkingDirectory=/srv/site1
RuntimeDirectory=site1
Environment="PATH=/srv/site1/.venv/bin"
ExecStartPre=/srv/site1/.venv/bin/python manage.py migrate --noinput
ExecStart=/srv/site1/.venv/bin/gunicorn -c /srv/site1/gunicorn.conf.py site1.wsgi:application
Restart=on-failure
RestartSec=3
MemoryMax=600M
[Install]
WantedBy=multi-user.targetRuntimeDirectory=site1 آغاز پر /run/site1 بناتا ہے اور بند ہوتے وقت اسے حذف کر دیتا ہے، اس لیے socket path ہمیشہ درست owner کے ساتھ موجود رہتا ہے۔ gunicorn config میں موجود umask = 0o007 لائن اس socket کو www-data group کے لیے writable بناتی ہے، اور اسی طرح nginx اس تک پہنچتا ہے۔
Flask unit وہی file ہے، صرف ایک لائن تبدیل ہوتی ہے: ExecStart=... gunicorn -c /srv/site1/gunicorn.conf.py app:app، اور ExecStartPre موجود نہیں ہوتا۔ app:app argument پہلے module اور پھر callable لیتا ہے، اس لیے Failed to find attribute 'app' in 'app'. error کا مطلب ہے کہ آپ کے module میں اس نام کا variable موجود نہیں ہے۔ Scheduled work بھی اسی pattern پر چلتا ہے، اور Django management command کے لیے systemd timer استعمال کرنا cron کی جگہ لے لیتا ہے، بغیر اس سائز کے server پر task queue شامل کیے۔
Static files
`DEBUG = False کے ساتھ Django کوئی static files خود serve نہیں کرتا۔ STATIC_ROOT set کریں، python manage.py collectstatic چلائیں، اور web server کو output directory کی طرف point کریں۔ یہ مرحلہ چھوڑنے پر admin بغیر styling کے load ہوتا ہے اور log Not Found: /static/admin/css/base.css` سے بھر جاتا ہے۔
انہیں serve کرنے کے دو مناسب طریقے ہیں۔ nginx کا alias block آپ کی app پر کوئی اضافی بوجھ نہیں ڈالتا۔ Middleware کے طور پر شامل کیا گیا WhiteNoise worker سے files serve کرتا ہے اور nginx block کی ضرورت ختم کر دیتا ہے، لیکن ہر file کے لیے worker کا تھوڑا وقت استعمال ہوتا ہے۔ Flask development میں اپنا static/ folder serve کرتا ہے، جبکہ production میں اسی وجہ سے proxy کو اس folder کی طرف point کیا جاتا ہے۔
Reverse proxy
server {
listen 80;
server_name example.com;
location /static/ {
alias /srv/site1/static/;
expires 30d;
}
location / {
proxy_pass http://unix:/run/site1/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}کسی بھی proxy کے پیچھے Django کو بتانا ضروری ہے کہ اصل request HTTPS تھی، ورنہ اس کی cross site request forgery (CSRF) checks آپ کے اپنے forms کو reject کر دیتی ہیں۔
ALLOWED_HOSTS = ["example.com"]
CSRF_TRUSTED_ORIGINS = ["https://example.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")اگر server پر پہلے ہی containers چل رہے ہیں تو کئی Docker Compose apps کے سامنے Traefik یہی کام ہر site کے لیے الگ file کے بجائے container پر labels کے ذریعے انجام دیتا ہے۔
Which database
ایک single application server کے لیے، جہاں write rate چند writes فی second ہو، SQLite واقعی مناسب ہے، اور یہ memory budget سے ایک مکمل daemon کا بوجھ ہٹا دیتا ہے۔ write ahead logging (WAL) فعال کریں اور driver کو busy timeout دیں۔
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
"OPTIONS": {
"timeout": 20,
"init_command": "PRAGMA journal_mode=WAL;",
},
}
}ان دونوں options کے بغیر پہلی بار دو workers بیک وقت write کریں تو django.db.utils.OperationalError: database is locked کا سامنا ہوتا ہے، کیونکہ default journal mode write کے دوران readers کو block کرتا ہے اور default timeout تقریباً فوراً دستبردار ہو جاتا ہے۔ اس بارے میں تفصیلی وضاحت، بشمول یہ کہ SQLite کب درست انتخاب نہیں رہتا، VPS پر SQLite کو production میں چلانے میں موجود ہے۔
اسی 1 GB server پر PostgreSQL اوپر دیے گئے budget میں 120 MB استعمال کرتا ہے، اور ہر persistent connection کے لیے ایک backend process بھی درکار ہوتا ہے۔ Django کا CONN_MAX_AGE ہر worker کے لیے ایک connection کھلا رکھتا ہے، اس لیے چار workers کا مطلب چار backends ہے۔ یہ عموماً مناسب trade-off ہے۔ صرف worker number مقرر کرنے سے پہلے اسے شمار کر لیں۔ اگر آپ database کو app کے ساتھ والے container میں رکھنا چاہتے ہیں تو VPS پر Docker چلانا اسی trade-off کی ایک صورت ہے، لیکن اس میں بنیادی overhead زیادہ ہے، کیونکہ daemon اور ہر container اس سائز کے server پر قابلِ ذکر اضافی بوجھ ڈالتے ہیں۔
بیٹریاں کیا سہولت دیتی ہیں اور ان کی قیمت کیا ہے
Django کے اضافی میگا بائٹس دراصل ان اجزا کی فہرست ہیں جو پہلے سے موجود ہیں اور ایک دوسرے کے ساتھ پہلے ہی کام کرتے ہیں: migrations کے ساتھ ORM، session اور authentication system، permission model، CSRF protection کے ساتھ form layer، template engine، management commands، اور admin۔ admin وہ جزو ہے جس کی قدر کو لوگ کم سمجھتے ہیں۔ یہ آپ کے models کے لیے ایک فعال database editor ہے، جس میں search اور filters شامل ہیں، اور اس کے لیے INSTALLED_APPS میں صرف ایک لائن درکار ہے۔
Flask اس کے بالکل برعکس ہے۔ آپ کو routing، request object، Jinja2 templates اور config object ملتے ہیں۔ باقی ہر چیز کا انتخاب آپ خود کرتے ہیں۔ جب app چھوٹی ہو تو یہ حقیقی فائدہ ہے، کیونکہ اگر آپ کسی ORM کو import ہی نہ کریں تو وہ load بھی نہیں ہوتا۔
مسئلہ درمیانی راستے میں پیدا ہوتا ہے۔ models کے لیے SQLAlchemy، migrations کے لیے Alembic، sessions کے لیے Flask-Login، forms اور CSRF کے لیے Flask-WTF، اور back office کے لیے ایک admin extension شامل کریں، تو آپ ایسی چیز بنا لیتے ہیں جس کا memory profile Django جیسا ہوتا ہے لیکن اس میں Django جیسا باہمی ربط نہیں ہوتا۔ ہر جزو کا اپنا release cycle اور app کو جوڑنے کا اپنا طریقہ ہوتا ہے۔ یہی وہ مقام ہے جہاں Django کم لاگت والا انتخاب بن جاتا ہے، RAM کے لحاظ سے بھی اور upgrades پر صرف ہونے والے وقت کے لحاظ سے بھی۔
Django بمقابلہ Flask: فیصلہ کرنے کا اصول
جب ایپ میں user accounts، قابلِ ترمیم content، ایسا schema جو مسلسل تبدیل ہوتا رہے، اور ایسا back office شامل ہو جسے کوئی حقیقتاً استعمال کرے، تو Django استعمال کریں۔ جب ایپ پہلے سے موجود datastore کے لیے JSON interface ہو، یا ایسا webhook receiver ہو جس میں HTML شامل نہ ہو، تو Flask استعمال کریں۔
حتمی فیصلہ تحریری فہرست کی بنیاد پر کریں۔ Flask میں مطلوبہ feature set حاصل کرنے کے لیے جو packages آپ install کریں گے، ان سب کی فہرست بنائیں۔ اگر اس فہرست میں ORM اور migration tool شامل ہیں، تو آپ پہلے ہی Django منتخب کر چکے ہیں اور وہاں آہستہ پہنچنے کے لیے اضافی کام کر رہے ہیں۔
چھوٹے hardware پر ایک صورت میں Flask واقعی بہتر انتخاب ہے: ایک ہی box پر متعدد چھوٹی services چلانا۔ ہر Flask service اپنی الگ کم وسائل استعمال کرنے والی process ہوتی ہے اور اپنی unit کے تحت چلتی ہے۔ ایک 1 GB VPS پر 3 Django sites چلانے کا مطلب ہے کہ framework کی 3 copies بیک وقت memory میں موجود ہوں گی، اور اوپر دیا گیا حساب قابلِ عمل نہیں رہتا۔ اگر حساب بار بار ناکافی ثابت ہو، تو زیادہ بڑا plan عموماً دیانت دارانہ حل ہوتا ہے، اور VPS کی ماہانہ اصل لاگت کیا ہے کے بارے میں گفتگو کسی working application کو دوبارہ لکھنے سے مختصر ہوتی ہے۔
وہ خرابی کی حالتیں اور ان کے ساتھ نظر آنے والے strings
Workers غائب ہو کر واپس آتے ہیں۔ Gunicorn [ERROR] Worker (pid:1234) was sent SIGKILL! Perhaps out of memory? لکھتا ہے۔ یہ اس وقت بھی لکھا جاتا ہے جب timeout heartbeat نہ ملنے پر یہ کسی worker کو ختم کرتا ہے، اور اس وقت بھی جب kernel نے process ختم کیا ہو۔ دونوں صورتوں میں فرق dmesg -T | grep -i "killed process" سے معلوم کریں۔ اگر وہاں کوئی line موجود ہو تو مسئلہ memory کا ہے؛ workers کی تعداد کم کریں یا swap شامل کریں۔
ہر page 400 واپس کرتا ہے اور log میں Invalid HTTP_HOST header لکھا ہے۔ مکمل message یہ ہے: Invalid HTTP_HOST header: 'example.com'. You may need to add 'example.com' to ALLOWED_HOSTS.۔ Django request کو آپ کے code تک پہنچنے سے پہلے مسترد کر دیتا ہے، کیونکہ ALLOWED_HOSTS خالی ہے یا اس میں وہ نام شامل نہیں جو proxy نے Host میں بھیجا تھا۔
Forms Origin checking failed کے ساتھ fail ہوتے ہیں۔ Page پر لکھا ہوتا ہے کہ CSRF verification ناکام ہو گئی۔ یہ مسئلہ TLS terminating proxy کے پیچھے پیش آتا ہے: app کو plain HTTP نظر آتا ہے، وہ http:// origin بناتی ہے، اور اس کا موازنہ اس request سے کرتی ہے جو https:// کے ذریعے پہنچی تھی۔ SECURE_PROXY_SSL_HEADER اور CSRF_TRUSTED_ORIGINS set کریں، اور تصدیق کریں کہ proxy واقعی X-Forwarded-Proto بھیجتا ہے۔
nginx فوری طور پر 502 واپس کرتا ہے۔ Error log وجہ بتاتا ہے: connect() to unix:/run/site1/gunicorn.sock failed (2: No such file or directory) کا مطلب ہے کہ unit چل نہیں رہی، جبکہ (13: Permission denied) کا مطلب ہے کہ socket موجود ہے لیکن nginx اسے کھول نہیں سکتا؛ مسئلہ umask اور group setting میں ہے۔
Admin میں styling موجود نہیں ہے۔ collectstatic نہیں چلایا گیا، یا alias path، STATIC_ROOT سے مطابقت نہیں رکھتا۔ Access log میں /static/admin/ کے تحت 404s نظر آتے ہیں۔
ہلکے load کے باوجود writes fail ہوتی ہیں۔ SQLite کا database is locked اس بات کی نشاندہی کرتا ہے کہ WAL بند ہے یا busy timeout اتنا کم ہے کہ ایک ہی وقت میں لکھنے والے دو workers کے لیے کافی نہیں۔
FAQ
کیا 1 GB VPS کے لیے Django بہت بھاری ہے؟
نہیں۔ کم تعداد میں workers، سامنے nginx، اور پیچھے SQLite کے ساتھ Django، 1 GB پر آسانی سے چلتا ہے۔ مسئلہ اس وقت پیدا ہوتا ہے جب اسی server پر PostgreSQL کو اس کی default settings کے ساتھ، cache، background worker اور Docker بھی شامل کر دیے جائیں۔ ایک worker کا proportional set size ناپیں، request peaks کے لیے اسے دوگنا کریں، اور کل مقدار کا موازنہ اس memory سے کریں جو operating system اور database کے بعد باقی رہتی ہے۔
ایک vCPU پر مجھے gunicorn کے کتنے workers چلانے چاہییں؟
تین سے شروع کریں اور پیمائش کریں۔ چھوٹے VPS پر عموماً memory بنیادی رکاوٹ ہوتی ہے، اس لیے operating system اور database کے بعد باقی RAM کو ایک worker کے proportional set size کے دوگنے پر تقسیم کریں۔ اگر آپ کی views زیادہ تر database یا upstream API کا انتظار کرتی ہیں تو کم تعداد میں workers اور ہر worker کے لیے کئی threads کے ساتھ gthread worker class استعمال کریں۔ threads framework کی ایک ہی loaded copy مشترک طور پر استعمال کرتے ہیں اور اضافی processes کے مقابلے میں بہت کم memory لیتے ہیں۔
کیا مجھے PostgreSQL درکار ہے، یا SQLite کافی ہے؟
ایک application server اور معمولی write rate کے لیے SQLite کافی ہے، اور یہ memory budget سے ایک مکمل daemon خارج کر دیتا ہے۔ write ahead logging فعال کریں اور busy timeout مقرر کریں، ورنہ concurrent writes database is locked کے ساتھ ناکام ہو جاتی ہیں۔ PostgreSQL پر اس وقت منتقل ہوں جب ایک سے زیادہ machines کو write کرنا ہو، یا جب آپ کو ایسی سہولت درکار ہو جو SQLite فراہم نہیں کرتا، مثلاً concurrent heavy writers یا per-role access control۔
کیا مجھے gunicorn کے بجائے uvicorn چلانا چاہیے؟
صرف اس وقت جب آپ کے پاس async views ہوں اور انتظار کے لیے کوئی حقیقی external operation موجود ہو۔ Flask ایک WSGI application ہے، اس لیے async view worker thread کے اندر ایک نئے event loop میں چلتی ہے اور اگلی request شروع ہونے سے پہلے مکمل ہو جاتی ہے۔ اس سے اضافی concurrency حاصل نہیں ہوتی۔ Django async views کے لیے بھی ASGI server درکار ہے، تب ہی وہ مددگار ثابت ہوتا ہے۔ حالیہ uvicorn releases نے اپنی gunicorn worker class کو ایک الگ package میں منتقل کر دیا ہے، اس لیے کسی پرانے tutorial سے پرانا worker class flag نقل کرنے کے بجائے موجودہ uvicorn documentation پڑھیں۔
میرا worker بغیر traceback کے کیوں ختم ہو گیا؟
Kernel کے out of memory killer کے ہاتھوں ختم ہونے والے process کو SIGKILL ملتا ہے۔ وہ ختم ہوتے وقت کچھ بھی log نہیں کر سکتا، اس لیے آپ کا application log اچانک رک جاتا ہے۔ Gunicorn اس وقفے کو محسوس کرتا ہے اور Worker (pid:1234) was sent SIGKILL! Perhaps out of memory? دکھاتا ہے۔ dmesg -T | grep -i "killed process" سے اس کی تصدیق کریں۔ حل یہ ہے کہ workers کی تعداد کم کریں، یا swapfile بنائیں تاکہ memory spike سے process ختم ہونے کے بجائے request سست ہو جائے۔