SSD Nodes Learn 8GB RAM — $66/سال
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-01

Docker Compose میں memory limit لگا کر OOM روکیں

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 (control group، یعنی kernel کی وہ سہولت جو عملوں کے مجموعے کے وسائل کی پیمائش کرتی ہے) پر عائد کرتا ہے۔ کسی service میں deploy.resources.limits.memory مقرر کرنے سے وہ container کبھی بھی آپ کی لکھی ہوئی مقدار سے زیادہ memory استعمال نہیں کر سکتا۔ جب وہ اس حد سے تجاوز کرنے کی کوشش کرتا ہے تو kernel، container کے اندر موجود کسی process کو ختم کر دیتا ہے، اور container عموماً code 137 کے ساتھ بند ہو جاتا ہے۔

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

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

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

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

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

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

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

mem_limit، mem_reservation، memswap_limit، cpus اور cpu_shares، 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 سے مخصوص حصے اس کی دوسری keys ہیں: mode، placement، update_config اور endpoint_mode، docker stack deploy کے لیے معنی رکھتے ہیں اور docker compose up انہیں نظرانداز کرتا ہے۔ اس لیے یہ عام مشورہ کہ "deploy کے لیے Swarm ضروری ہے"، resources subsection کے لیے غلط ہے۔ اس مشورے پر عمل کرنے سے آپ کی services پر کوئی limit لاگو نہیں ہوتی۔

ہر project کے لیے ایک ہی املا منتخب کریں۔ ایک ہی 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 کو 1073741824 کے طور پر دکھایا جاتا ہے۔ CPU کی value nano CPUs میں ہوتی ہے، اس لیے 1.5 کو 1500000000 کے طور پر دکھایا جاتا ہے۔ کسی بھی field میں 0 کا مطلب ہے کہ کوئی limit مقرر نہیں کی گئی۔ Docker جس کم ترین memory limit کو قبول کرتا ہے وہ 6m ہے، اور اس سے کم limit پر container start ہونے سے انکار کر دیتا ہے۔

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

container سست نہیں ہوتا۔ یہ بند ہو جاتا ہے۔

جب کوئی process کسی page کی درخواست کرتا ہے اور cgroup پہلے ہی اپنی memory.max حد پر ہو، تو kernel پہلے اسی cgroup کے اندر ممکنہ وسائل واپس حاصل کرتا ہے: پہلے صاف 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 کا مطلب ہے کہ کسی اور چیز نے SIGKILL بھیجا، اور عام وجہ یہ ہے کہ docker compose stop نے اپنی دس سیکنڈ کی grace period پوری کر لی کیونکہ app نے SIGTERM کو نظرانداز کیا۔ یہ فرق کئی گھنٹے بچاتا ہے، کیونکہ ان دونوں مسائل کا آپس میں کوئی تعلق نہیں۔

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

docker events --filter event=oom

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

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

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

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

Reservation ایک اشارہ ہے، limit قاعدہ ہے

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

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

ایماندارانہ طور پر swap کا حساب

زیادہ تر VPS images میں بالکل بھی swap file موجود نہیں ہوتی۔ swapon --show اور free -h چلائیں۔ اگر swap کا کل حجم صفر ہے تو ذیل کی swap سے متعلق تمام settings بے اثر رہیں گی، اور آپ کی 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 کے بغیر کام کرتا ہے۔ پرانا پیغام 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 اس machine کی ہر دوسری service کو سست کر دیتا ہے۔ latency کے لیے حساس workloads میں درست limit کے ساتھ swap نہ ہونے سے failure زیادہ تیزی اور قابل پیش گوئی انداز میں ہوتا ہے۔

میموری کا استعمال حقیقت سے زیادہ خراب کیوں دکھائی دیتا ہے

docker stats میں موجود MEM USAGE کے اعداد و شمار میں page cache بھی شامل ہوتا ہے۔ اس لیے بڑے فائلیں پڑھنے والا container اپنی حد کے قریب پہنچ کر وہیں برقرار رہتا ہے۔ یہ معمول کی بات ہے اور memory leak نہیں ہے، کیونکہ OOM killer کو بلانے سے پہلے clean cache دوبارہ حاصل کر لیا جاتا ہے۔ اسی وجہ سے خود میزبانی کیا ہوا Jellyfin میڈیا سرور مستقل طور پر اپنی حد کے قریب دکھائی دے گا۔

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 ہے، جسے خارج کیا جا سکتا ہے۔ اپنی حد کا تعین کل مقدار کے بجائے 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 کا مجموعہ اس سے کم رہنا چاہیے۔ Overcommitting اس دن تک کام کرتا ہے جب تک دو services ایک ساتھ peak نہ کریں۔

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

  • 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 ہوگا۔

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

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

CPU کی حدود بالکل مختلف طریقے سے کام کرتی ہیں

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

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

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

FAQ

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

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

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

اس کا مطلب ہے کہ مرکزی 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 مقرر کرنا چاہیے؟

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

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

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

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

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