SSD Nodes Learn Hosting plans →
تعلیمی Matt Connorتحریر: Matt Connor

Docker Compose میں OOM روکنے کے لیے memory limits

Docker Compose میں memory اور CPU limits لگائیں تاکہ ایک container آپ کے VPS کو بند نہ کرے۔ deploy.resources، mem_limit، exit 137، swap اور sizing کی وضاحت۔

Docker Compose کی memory limit کیا کرتی ہے

Docker Compose کی memory limit وہ سخت حد ہے جو Linux kernel ایک container کے cgroup پر عائد کرتا ہے۔ cgroup سے مراد control group ہے، یعنی kernel کی وہ سہولت جو processes کے کسی مجموعے کے وسائل کی پیمائش کرتی ہے۔ کسی service پر deploy.resources.limits.memory مقرر کرنے کے بعد وہ container اس میں درج مقدار سے زیادہ memory استعمال نہیں کر سکتا۔ جب container اس حد سے تجاوز کرنے کی کوشش کرتا ہے تو kernel اس کے اندر موجود کسی process کو ختم کر دیتا ہے، اور container عموماً code 137 کے ساتھ بند ہو جاتا ہے۔

یہ خاص طور پر VPS پر اہم ہے، جہاں RAM محدود ہوتی ہے اور host کی اضافی memory دستیاب نہیں ہوتی۔ memory leak یا خراب query رکھنے والا ایک container 8GB والے server کی تمام خالی memory استعمال کر سکتا ہے۔ اس کے بعد kernel عموماً database یا آپ کے SSH session جیسے کسی process کو ختم کر دیتا ہے، نہ کہ لازماً اس container کو جس نے مسئلہ پیدا کیا ہو۔ Limits پورے server کی خرابی کو ایک ایسی service تک محدود کر دیتی ہیں جو دوبارہ start ہو سکتی ہے۔

services:
  app:
    image: ghcr.io/example/app:1.4
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: 1g
        reservations:
          memory: 256m

اسے apply کریں اور تصدیق کریں کہ limit فعال ہے:

docker compose up -d
docker stats --no-stream

MEM USAGE / LIMIT column میں تقریباً 142MiB / 1GiB جیسی value دکھائی دینی چاہیے۔ اگر limit column میں host کی پوری RAM دکھائی دے تو setting apply نہیں ہوئی۔ اس وقت تک اس guide کے باقی حصے مددگار نہیں ہوں گے جب تک یہ setting apply نہ ہو جائے۔ اگر compose file آپ کے لیے نئی ہے تو VPS کے لیے Docker Compose کی بنیادی معلومات اس file layout کی وضاحت کرتی ہیں جس پر یہ guide مبنی ہے۔

deploy.resources.limits یا mem_limit: کون سا لاگو ہوتا ہے

ایک ہی تصور کے لیے دو ہجے موجود ہیں، اسی لیے یہ معاملہ confusing ہے۔

mem_limit، mem_reservation، memswap_limit، cpus اور cpu_shares top-level service keys ہیں جو پرانے Compose file formats سے inherited ہیں۔ deploy.resources Swarm schema سے آیا تھا اور اب Compose Specification کا حصہ ہے، یعنی وہ format جسے docker compose آج پڑھتا ہے۔

دونوں ایک ہی host پر کام کرتے ہیں۔ Docker Compose، یعنی docker compose plugin، جب آپ docker compose up چلاتے ہیں تو deploy.resources.limits اور deploy.resources.reservations لاگو کرتا ہے؛ اس کے لیے کہیں بھی Swarm cluster موجود ہونا ضروری نہیں۔ یہ plugin اب اپنی v5 لائن میں ہے۔ 2026-10-06 کو جانچ کے وقت v5.6.0 latest stable release تھا۔ یہ v2 سے براہ راست v5 پر گیا اور 3 اور 4 کو چھوڑ دیا، تاکہ پرانے Compose file format versions کے ساتھ confusion نہ ہو۔ v5 کی کسی release نے resource limits کے طریقۂ کار کو تبدیل نہیں کیا۔ deploy block کے صرف دوسرے keys Swarm تک محدود ہیں: mode، placement، update_config اور endpoint_mode، docker stack deploy کے لیے معنی رکھتے ہیں اور docker compose up انہیں ignore کرتا ہے۔ اس لیے یہ عام مشورہ کہ "deploy کو Swarm درکار ہے"، resources subsection کے لیے غلط ہے۔ اس مشورے پر عمل کرنے سے آپ کی services پر کوئی limit لاگو نہیں ہوتی۔

ہر project کے لیے ایک ہی spelling منتخب کریں۔ ایک ہی service میں mem_limit: 512m اور deploy.resources.limits.memory: 1g دونوں لکھنے سے ایسی file بنتی ہے جسے ایک نظر میں سمجھنا مشکل ہوتا ہے۔ اندازہ لگانے کے بجائے daemon سے پوچھیں:

docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1

Memory values bytes میں ہوتی ہیں، اس لیے 1g کا output 1073741824 ہوتا ہے۔ CPU کی قدر nano CPUs میں ہوتی ہے، اس لیے 1.5 کا output 1500000000 ہوتا ہے۔ کسی بھی field میں 0 کا مطلب ہے کہ کوئی limit set نہیں کی گئی۔ Docker کم از کم 6m کی memory limit قبول کرتا ہے؛ اس سے کم limit ہونے پر container start نہیں ہوتا۔

جب کوئی container limit تک پہنچتا ہے

container کی رفتار کم نہیں ہوتی۔ یہ ختم ہو جاتا ہے۔

جب کوئی process page طلب کرتا ہے اور cgroup پہلے ہی اپنی memory.max حد پر ہوتا ہے، تو kernel پہلے اسی cgroup کے اندر ممکنہ وسائل واپس حاصل کرتا ہے: clean page cache، پھر وہ pages جنہیں swap کیا جا سکتا ہے۔ اگر reclaim سے کافی جگہ خالی نہ ہو، تو cgroup کا OOM (out of memory) killer container کے اندر موجود کسی process کو منتخب کر کے اسے SIGKILL بھیج دیتا ہے۔ container کے PID 1 کو ختم کرنے سے container بھی ختم ہو جاتا ہے۔ Exit code 137 دراصل 128 جمع signal 9 ہے، اس لیے 137 کسی بھی SIGKILL کی علامت ہے، خود OOM کا قطعی ثبوت نہیں۔

docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1

true 137 کا مطلب OOM kill ہے۔ false 137 کا مطلب ہے کہ کسی اور process نے SIGKILL بھیجا، اور اس کی عام وجہ یہ ہے کہ docker compose stop نے اپنی دس سیکنڈ کی grace period پوری کر لی کیونکہ app نے SIGTERM نظرانداز کر دیا۔ یہ فرق کئی گھنٹے بچا سکتا ہے، کیونکہ دونوں مسائل کا ایک دوسرے سے کوئی تعلق نہیں۔

یہ واقعہ مزید دو مقامات پر درج ہوتا ہے۔ daemon کو براہ راست دیکھیں:

docker events --filter event=oom

پھر kernel log پڑھیں، کیونکہ restart کے بعد بھی یہی ریکارڈ باقی رہتا ہے:

sudo dmesg -T | grep -i -E 'memory cgroup out of memory|killed process'

cgroup kill ایسی line لکھتا ہے جس کا آغاز Memory cgroup out of memory: Killed process 24713 (node) سے ہوتا ہے۔ Memory cgroup prefix کے بغیر line host OOM کی نشاندہی کرتی ہے، یعنی پوری machine کی RAM ختم ہو گئی۔ Limits کا مقصد اسی failure کو روکنا ہے، اس لیے اس کا ظاہر ہونا بتاتا ہے کہ آپ کی limits کا مجموعہ بہت زیادہ ہے، یا کچھ services پر کوئی limit مقرر نہیں۔

restart: unless-stopped کے ساتھ OOM loop آسانی سے چھپ سکتا ہے، کیونکہ service ختم ہونے کے ایک سیکنڈ بعد docker compose ps میں دوبارہ running دکھائی دیتی ہے۔ uptime column اور restart count دیکھیں، اور limit کو ایسے healthcheck کے ساتھ جوڑیں جو app کو unhealthy رپورٹ کرے تاکہ بار بار ختم ہونے والا container آپ کی نگرانی کے بغیر بھی نمایاں رہے۔

Reservation صرف ایک اشارہ ہے، limit اصول ہے

reservations.memory (پرانا mem_reservation) ایک soft floor ہے۔ Docker اسے ایسی soft limit قرار دیتا ہے جو daemon کو host پر contention یا کم memory محسوس ہونے پر فعال ہوتی ہے۔ یہ container کو اس سے زیادہ memory استعمال کرنے سے کبھی نہیں روکتی، اور نہ ہی یہ ضمانت دیتی ہے کہ container کی درخواست کے وقت memory دستیاب ہوگی۔ یہ صرف kernel کو اس بات کی طرف مائل کرتی ہے کہ پہلے ان containers سے memory reclaim کرے جو اپنی reservation سے زیادہ استعمال کر رہے ہوں۔

اس لیے reservation اپنے طور پر کسی چیز کا تحفظ نہیں کرتی۔ اسے ایسی service کی نشان دہی کے لیے استعمال کریں جسے دباؤ کے وقت ترجیحی سلوک دینا چاہتے ہیں، اور safety کے لیے limit پر انحصار کریں۔ reservation کو limit سے کم رکھیں، ورنہ container start نہیں ہوگا: Docker configuration کو Minimum memory limit can not be less than memory reservation limit کے ساتھ reject کر دیتا ہے۔

Swap accounting، حقیقت پسندانہ جائزہ

زیادہ تر VPS images میں بالکل بھی swap file موجود نہیں ہوتی۔ swapon --show اور free -h چلائیں۔ اگر swap کی کل مقدار zero ہو تو نیچے دی گئی swap سے متعلق ہر setting بے اثر رہتی ہے، اور آپ کی memory limit صرف RAM کی حد ہوتی ہے۔

memswap_limit swap کی مقدار نہیں ہے۔ یہ memory اور swap کا مجموعہ ہے۔ mem_limit: 1g اور memswap_limit: 2g کے ساتھ container کو 1GB RAM اور 1GB swap ملتی ہے۔ دونوں values برابر مقرر کرنے سے container کے لیے کوئی swap باقی نہیں رہتی۔ mem_limit مقرر کرنے اور memswap_limit کو unset چھوڑنے سے container دوبارہ اپنی memory limit کے برابر swap استعمال کر سکتا ہے۔

Ubuntu 24.04 اور Debian 13 میں default طور پر cgroup v2 استعمال ہوتا ہے۔ اس میں swap ایک الگ counter (memory.swap.max) ہے، اور یہ اضافی setup کے بغیر کام کرتا ہے۔ پرانا message Your kernel does not support swap limit capabilities ان cgroup v1 hosts سے آتا ہے جو swapaccount=1 کے بغیر boot ہوئے ہوں۔ ان hosts پر memory limit لاگو رہتی ہے، لیکن swap والا حصہ نظرانداز کر دیا جاتا ہے۔

اس بارے میں حقیقت پسندانہ رہیں کہ swap سے کیا فائدہ ملتا ہے۔ یہ OOM kill کو کم ممکن نہیں بناتی، بلکہ اسے سست کرتی ہے، کیونکہ memory leak کرنے والا process RAM کی طرح swap بھی آسانی سے بھر دیتا ہے۔ اسی دوران shared VPS storage پر swap استعمال کرتے ہوئے ایک container مسلسل thrashing کرے تو server کی ہر دوسری service سست ہو جاتی ہے۔ latency-sensitive workloads کے لیے درست limit اور swap کے بغیر configuration زیادہ تیزی اور زیادہ قابل پیش گوئی انداز میں fail ہوتی ہے۔

میموری کا استعمال اصل سے زیادہ کیوں دکھائی دیتا ہے

docker stats میں موجود MEM USAGE کے اعداد میں page cache بھی شامل ہوتا ہے، اس لیے بڑی فائلیں پڑھنے والا container اپنی limit کے قریب پہنچ کر وہیں رہتا ہے۔ یہ معمول کی بات ہے اور memory leak نہیں ہے، کیونکہ OOM killer کے فعال ہونے سے پہلے clean cache دوبارہ حاصل کر لیا جاتا ہے۔ self-hosted Jellyfin media server جیسی service اسی وجہ سے مسلسل اپنی حد کے قریب دکھائی دے گی۔

container کے اندر سے اس عدد کو cache اور حقیقی working set میں تقسیم کریں:

docker compose exec app grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat
docker compose exec app cat /sys/fs/cgroup/memory.events

anon anonymous memory ہے، یعنی وہ working set جسے خارج نہیں کیا جا سکتا۔ file page cache ہے، جسے دوبارہ حاصل کیا جا سکتا ہے۔ اپنی limit کا تعین کل مقدار کے بجائے anon جمع مناسب اضافی گنجائش کی بنیاد پر کریں۔ memory.events فائل اس معاملے کو قطعی طور پر واضح کرتی ہے: صفر سے زیادہ oom_kill counter کا مطلب ہے کہ container شروع ہونے کے بعد kernel نے اس میں کسی چیز کو ختم کیا ہے، جبکہ max counter کا بڑھنا ظاہر کرتا ہے کہ container اس وقت اپنی حد تک پہنچا ہوا ہے۔ دونوں commands کے لیے image کے اندر shell اور coreutils درکار ہوتے ہیں، اس لیے یہ distroless یا scratch image پر ناکام ہو جاتی ہیں۔

8GB VPS پر وسائل کی حد مقرر کرنا

ایپس سے نہیں بلکہ host سے آغاز کریں۔ 8GB VPS پر kernel، Docker daemon، sshd، journald اور اپنے login shell کے لیے تقریباً 1GB چھوڑ دیں۔ اس طرح تقریباً 7GB دستیاب رہتا ہے، اور تمام containers کی limits کا مجموعہ اس مقدار سے کم رہنا چاہیے۔ ضرورت سے زیادہ وسائل مختص کرنا اس وقت تک کام کرتا ہے جب تک دو services بیک وقت peak پر نہ پہنچ جائیں۔

8GB سرور پر ایک قابلِ عمل تقسیم:

  • Reverse proxy: 128m limit۔ یہ ایک چھوٹا process ہے، اور اتنی سخت limit runaway configuration reload کو فوراً پکڑ لیتی ہے۔
  • PostgreSQL: 2g limit، اور database configuration میں shared_buffers تقریباً 512MB پر set کریں۔
  • Application container: 1g limit۔
  • Background worker: 512m limit۔
  • Media یا file service: 2g limit؛ اس میں سے زیادہ تر page cache ہوگا۔

ان اعداد کو اپنے stack میں براہِ راست استعمال نہ کریں۔ Services کو ایک دن حقیقی load کے تحت چلائیں، docker stats monitor کریں، ہر container کے لیے peak anon value نوٹ کریں، اور headroom کے طور پر اس میں تقریباً نصف مزید شامل کریں۔ بہت کم مقرر کی گئی limit، limit نہ ہونے سے بھی بدتر ہے، کیونکہ یہ معمول کے traffic spike کے دوران healthy service کو ختم کر دیتی ہے۔

ایک مسئلہ الگ وضاحت کا مستحق ہے۔ زیادہ تر runtimes اس limit کو اس وقت تک نہیں دیکھتے جب تک آپ انہیں اس کے بارے میں نہ بتائیں۔ PostgreSQL خوش دلی سے shared_buffers اور work_mem کو اپنی container limit سے زیادہ set کر دے گا اور process killed ہو جائے گا۔ JVM (Java virtual machine) کو -XX:MaxRAMPercentage=75 درکار ہوتا ہے تاکہ وہ اپنا heap host RAM کے بجائے cgroup limit کے مطابق set کرے۔ Node.js میں --max-old-space-size megabytes میں set کریں اور اسے container limit سے کم رکھیں، ورنہ اس کا garbage collector heap کو اس وقت تک بڑھنے دے گا جب تک kernel مداخلت نہ کرے۔ Ollama میں بھی یہی صورتِ حال ہے، صرف knob مختلف ہے، کیونکہ num_ctx بڑھانے سے KV cache کا حجم بڑھتا ہے اور طویل prompt کے دوران container ختم ہو جاتا ہے۔ cgroup مذاکرات نہیں کرتا۔ یہ process کو ختم کر دیتا ہے۔

CPU limits کا طریقۂ کار بالکل مختلف ہے

cpus: "1.5" کا مطلب ایک core کے 150% کے برابر ہے، جسے CFS (completely fair scheduler) quota کے طور پر نافذ کیا جاتا ہے۔ Container کو ہر 100ms کے period میں CPU time کے 150ms ملتے ہیں، اور یہ وقت اس کے تمام threads میں مشترک ہوتا ہے۔ جب یہ وقت ختم ہو جائے تو kernel اگلے period تک اسے انتظار کرواتا ہے۔

یہی بنیادی فرق ہے۔ Memory limit سے تجاوز کرنے والے container کو kill کر دیا جاتا ہے۔ CPU limit سے تجاوز کرنے والے container کو throttle کیا جاتا ہے، لیکن وہ کم رفتار سے کام جاری رکھتا ہے۔ اس لیے CPU limit نسبتاً جارحانہ انداز میں مقرر کرنا محفوظ ہے، جبکہ memory limit میں اضافی گنجائش رکھنا ضروری ہے۔

cpu_shares ایک مختلف tool ہے: یہ relative weight ہوتا ہے، جو صرف اس وقت اثرانداز ہوتا ہے جب CPUs واقعی مکمل طور پر مصروف ہوں۔ 1024 اور 512 shares والے دو containers ایک مصروف core کو تقریباً 2:1 کے تناسب سے تقسیم کرتے ہیں، جبکہ idle system پر ان میں سے کسی پر پابندی نہیں لگتی۔ Services کی اہمیت کے مطابق ترجیح مقرر کرنے کے لیے shares استعمال کریں، اور حقیقی maximum ceiling درکار ہو تو cpus استعمال کریں۔ مثال کے طور پر، رات کو چلنے والے transcode job کو web server کے وسائل ختم کرنے سے روکنے کے لیے۔

FAQ

کیا deploy.resources.limits Docker Swarm کے بغیر کام کرتا ہے؟

ہاں۔ Docker Compose، docker compose plugin (جو اب اپنی v5 لائن پر ہے)، ایک ہی host پر docker compose up چلانے کے وقت deploy.resources.limits اور deploy.resources.reservations نافذ کرتا ہے۔ docker inspect --format '{{.HostConfig.Memory}}' <container> سے اس کی تصدیق کریں۔ یہ limit کو bytes میں دکھاتا ہے اور جب کوئی limit نافذ نہ ہوئی ہو تو 0 دکھاتا ہے۔ deploy کے اندر موجود وہ keys جن کے لیے واقعی Swarm درکار ہے، mode، placement، update_config اور endpoint_mode ہیں۔

Docker Compose میں exit code 137 کا کیا مطلب ہے؟

اس کا مطلب ہے کہ main process کو SIGKILL موصول ہوا، کیونکہ 137، 128 جمع signal 9 کے برابر ہے۔ عام وجہ kernel کا OOM killer ہوتا ہے، لیکن جب کوئی app SIGTERM کو نظرانداز کرے تو shutdown timeout بھی یہی code پیدا کرتا ہے۔ دونوں وجوہات میں فرق کرنے کے لیے docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> چلائیں۔ true 137 memory kill ہے، جبکہ false 137 نہیں ہے۔

کیا مجھے mem_limit یا deploy.resources.limits.memory set کرنا چاہیے؟

docker compose کے ساتھ دونوں میں سے کوئی بھی کام کرتا ہے۔ deploy.resources.limits.memory موجودہ Compose Specification کی شکل ہے اور نئی file کے لیے بہتر default ہے۔ اگر آپ کی file کے باقی حصے میں پہلے ہی پرانی top-level keys استعمال ہو رہی ہوں تو mem_limit برقرار رکھیں۔ ایک ہی service پر دونوں set کرنے سے file صرف پڑھنے میں زیادہ مشکل ہوتی ہے، اس لیے ایک کا انتخاب کریں اور docker inspect سے نتیجے کی تصدیق کریں۔

میرا container kill ہوئے بغیر اپنی مکمل memory limit پر کیوں موجود ہے؟

docker stats میں usage figure میں page cache بھی شامل ہوتا ہے۔ kernel دباؤ کے وقت اسے drop کر دیتا ہے، بجائے اس کے کہ OOM kill شروع کرے۔ docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat چلائیں اور anon value پڑھیں۔ یہ وہ working set ہے جسے reclaim نہیں کیا جا سکتا۔ کم anon value کے ساتھ زیادہ file value اس بات کی نشاندہی کرتی ہے کہ container disk input اور output کر رہا ہے، نہ کہ مرنے کے قریب ہے۔

8GB VPS پر کتنی RAM غیر مختص چھوڑنی چاہیے؟

kernel، Docker daemon، sshd، journald اور اپنے shell کے لیے تقریباً 1GB چھوڑیں، پھر تمام container limits کا مجموعہ باقی 7GB سے کم رکھیں۔ حقیقی load کے دوران ہر container کی peak anon value کو ایک دن تک monitor کریں، پھر values طے کریں۔ کل مقدار کو بھرنے کے ہدف کے بجائے budget سمجھیں۔