SSD Nodes Learn Hosting plans →
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-29

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

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

Docker Compose میموری limit کیا کرتا ہے

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

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

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۔ اگر limit column میں host کی پوری RAM دکھائی دے تو setting apply نہیں ہوئی۔ جب تک یہ setting apply نہ ہو، اس guide کا باقی حصہ مدد نہیں کرے گا۔ اگر compose file آپ کے لیے نئی ہے تو VPS کے لیے Docker Compose کی بنیادی باتیں اس file layout کی وضاحت کرتی ہیں جس پر یہ configuration مبنی ہے۔

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

ایک ہی تصور کے لیے دو syntax موجود ہیں، اسی لیے یہ معاملہ الجھن پیدا کرتا ہے۔

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

دونوں ایک ہی host پر کام کرتے ہیں۔ Compose V2، یعنی docker compose plugin، جب آپ docker compose up چلاتے ہیں تو deploy.resources.limits اور deploy.resources.reservations لاگو کرتا ہے، چاہے کہیں بھی Swarm cluster موجود نہ ہو۔ deploy block کے صرف Swarm-specific حصے دوسری keys ہیں: mode، placement، update_config اور endpoint_mode۔ ان کا مطلب docker stack deploy کے لیے ہوتا ہے، جبکہ docker compose up انہیں نظرانداز کرتا ہے۔ اس لیے یہ عام مشورہ کہ "deploy کے لیے Swarm ضروری ہے" resources subsection کے معاملے میں غلط ہے۔ اس مشورے پر عمل کرنے سے آپ کی services پر کوئی limit عائد نہیں ہوتی۔

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

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

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

کنٹینر حد تک پہنچنے پر کیا ہوتا ہے

کنٹینر سست نہیں ہوتا۔ یہ بند ہو جاتا ہے۔

جب کوئی process کسی page کی درخواست کرتا ہے اور cgroup پہلے ہی اپنی memory.max حد پر ہو، تو kernel پہلے اسی cgroup کے اندر ممکنہ وسائل واپس حاصل کرتا ہے: پہلے clean page cache، پھر وہ pages جنہیں swap کیا جا سکتا ہے۔ اگر اس عمل سے کافی memory آزاد نہ ہو، تو cgroup کا OOM (out of memory) killer کنٹینر کے اندر موجود کسی process کو منتخب کر کے اسے SIGKILL بھیج دیتا ہے۔ کنٹینر کے PID 1 کو ختم کرنے سے کنٹینر بھی ختم ہو جاتا ہے۔ 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 کو براہِ راست monitor کریں:

docker events --filter event=oom

پھر kernel log پڑھیں، کیونکہ restart کے بعد بھی یہی record محفوظ رہتا ہے:

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 کو روکنا ہے، اس لیے ایسی line کا نظر آنا اس بات کی علامت ہے کہ آپ کی limits کا مجموعہ بہت زیادہ ہے یا کچھ services پر کوئی limit مقرر نہیں۔

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

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 کے ساتھ مسترد کر دیتا ہے۔

Swap accounting، حقیقت پسندانہ انداز میں

زیادہ تر VPS images میں بالکل بھی swap file موجود نہیں ہوتی۔ swapon --show اور free -h چلائیں۔ اگر swap کا کل حجم صفر ہے تو نیچے دی گئی 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 set کرنے اور 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 استعمال کرتے ہوئے thrash کرنے والا container server کی ہر دوسری service کو سست کر دیتا ہے۔ latency-sensitive کاموں کے لیے درست limit کے ساتھ swap نہ رکھنا زیادہ تیزی اور زیادہ قابل پیش گوئی انداز میں failure پیدا کرتا ہے۔

میموری کا استعمال حقیقت سے زیادہ کیوں نظر آتا ہے

MEM USAGE کا اعداد و شمار docker stats میں 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 جسے drop نہیں کیا جا سکتا۔ file page cache ہے، جسے drop کیا جا سکتا ہے۔ اپنی limit anon اور اضافی گنجائش کی بنیاد پر مقرر کریں، کل memory کے حساب سے نہیں۔ memory.events فائل اس بحث کو قطعی طور پر ختم کر دیتی ہے: oom_kill counter کا صفر سے زیادہ ہونا ظاہر کرتا ہے کہ container کے شروع ہونے کے بعد kernel نے اس میں کسی process کو kill کیا ہے، جبکہ max counter کا بڑھنا ظاہر کرتا ہے کہ container اس وقت اپنی حد پر چل رہا ہے۔ دونوں commands کے لیے image کے اندر shell اور coreutils درکار ہوتے ہیں، اس لیے distroless یا scratch image پر یہ fail ہو جاتی ہیں۔

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

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

8GB machine کے لیے ایک قابلِ عمل تقسیم:

  • Reverse proxy: 128m limit۔ یہ ایک چھوٹا process ہے، اور اتنی سخت limit runaway config reload کو فوراً ظاہر کر دیتی ہے۔
  • PostgreSQL: 2g limit، اور database config میں 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 نہ ہونے سے بھی بدتر ہے، کیونکہ معمول کے network traffic spike کے دوران یہ صحت مند service کو ختم کر دیتی ہے۔

ایک مسئلہ الگ وضاحت کا مستحق ہے۔ زیادہ تر runtimes کے لیے limit اس وقت تک پوشیدہ رہتی ہے جب تک آپ انہیں اس کے بارے میں نہ بتائیں۔ PostgreSQL shared_buffers اور work_mem کو خوشی سے اپنے container limit سے بڑھا دے گا اور پھر killed ہو جائے گا۔ JVM (Java virtual machine) کو -XX:MaxRAMPercentage=75 درکار ہوتا ہے تاکہ وہ اپنا heap host RAM کے بجائے cgroup limit کے مطابق مقرر کرے۔ Node.js کو megabytes میں --max-old-space-size درکار ہوتا ہے۔ اسے 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 میں 150ms کا CPU وقت ملتا ہے، جو اس کے تمام threads کے درمیان تقسیم ہوتا ہے۔ جب یہ وقت ختم ہو جاتا ہے تو kernel اسے اگلے period تک انتظار کرواتا ہے۔

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

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

FAQ

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

جی ہاں۔ جب آپ ایک ہی host پر docker compose up چلاتے ہیں تو Compose V2، 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 کا کیا مطلب ہے؟

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

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

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

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

docker stats میں usage figure میں page cache بھی شامل ہوتا ہے۔ pressure بڑھنے پر kernel OOM kill شروع کرنے کے بجائے page cache کو خارج کر دیتا ہے۔ 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 چھوڑیں۔ اس کے بعد تمام containers کی limits کا مجموعہ باقی 7GB سے کم رکھیں۔ حقیقی load کے دوران ایک دن تک ہر container کی peak anon value monitor کریں، پھر numbers مقرر کریں۔ کل مقدار کو بھرنے کے ہدف کے بجائے budget سمجھیں۔