SSD Nodes Learn 8GB RAM — $66/साल
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-01

Docker Compose में मेमोरी लिमिट कैसे सेट करें

Docker Compose में deploy.resources का उपयोग करके मेमोरी और CPU लिमिट सेट करना सीखें। यह गाइड आपको exit 137 एरर से बचने और कंटेनर को VPS क्रैश करने से रोकने में मदद करेगी।

Docker Compose मेमोरी लिमिट क्या करती है

Docker Compose मेमोरी लिमिट एक कठोर सीमा है जिसे Linux कर्नल किसी कंटेनर के cgroup (कंट्रोल ग्रुप, वह कर्नल फीचर जो प्रक्रियाओं के एक समूह के लिए संसाधनों को मापता है) पर लागू करता है। किसी सर्विस पर deploy.resources.limits.memory सेट करें और वह कंटेनर आपके द्वारा लिखी गई संख्या से अधिक मेमोरी का उपयोग कभी नहीं कर पाएगा। जब वह ऐसा करने का प्रयास करता है, तो कर्नल कंटेनर के अंदर की एक प्रक्रिया को समाप्त (kill) कर देता है, और कंटेनर आमतौर पर कोड 137 के साथ बंद हो जाता है।

यह VPS पर सबसे अधिक मायने रखता है, जहाँ RAM निश्चित होती है और उधार लेने के लिए कोई अतिरिक्त होस्ट मेमोरी नहीं होती। मेमोरी लीक या खराब क्वेरी वाला एक कंटेनर 8GB वाले बॉक्स के हर खाली पेज को भर देगा। इसके बाद कर्नल उस प्रक्रिया को समाप्त कर देता है जिसे वह सबसे खराब मानता है, जो अक्सर समस्या पैदा करने वाले कंटेनर के बजाय डेटाबेस या आपका SSH सत्र होता है। सीमाएं पूरे सर्वर के आउटेज को केवल एक सर्विस के रीस्टार्ट में बदल देती हैं।

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

इसे लागू करें और पुष्टि करें कि लिमिट सक्रिय है:

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

MEM USAGE / LIMIT कॉलम में 142MiB / 1GiB जैसा कुछ दिखाई देना चाहिए। यदि लिमिट कॉलम में पूरी होस्ट RAM दिखाई देती है, तो सेटिंग लागू नहीं हुई है, और यह गाइड तब तक काम नहीं करेगी जब तक यह लागू न हो जाए। यदि compose फ़ाइल आपके लिए नई है, तो VPS के लिए Docker Compose की बुनियादी बातें उस फ़ाइल लेआउट को कवर करती हैं जिस पर यह आधारित है।

deploy.resources.limits या mem_limit: कौन सा लागू होता है

एक ही विचार के लिए दो वर्तनी मौजूद हैं, जो इसे भ्रमित करने वाला बनाती हैं।

mem_limit, mem_reservation, memswap_limit, cpus और cpu_shares पुराने Compose फ़ाइल स्वरूपों से विरासत में मिली टॉप-लेवल सर्विस कीज़ हैं। deploy.resources Swarm स्कीमा से आया था और अब Compose Specification का हिस्सा है, जो कि वह स्वरूप है जिसे docker compose आज पढ़ता है।

दोनों एक सिंगल होस्ट पर काम करते हैं। Compose V2, जो कि docker compose प्लगइन है, deploy.resources.limits और deploy.resources.reservations को लागू करता है जब आप docker compose up चलाते हैं, भले ही कहीं भी Swarm क्लस्टर न हो। deploy ब्लॉक के केवल-Swarm वाले हिस्से अन्य कीज़ हैं: mode, placement, update_config और endpoint_mode का अर्थ docker stack deploy के लिए कुछ होता है और इन्हें docker compose up द्वारा अनदेखा कर दिया जाता है। इसलिए यह आम सलाह कि "deploy के लिए Swarm की आवश्यकता है" resources सब-सेक्शन के लिए गलत है, और इसका पालन करने से आपकी सर्विसेज पर कोई सीमा (limit) नहीं रहती है।

प्रति प्रोजेक्ट एक वर्तनी चुनें। एक ही सर्विस पर mem_limit: 512m और deploy.resources.limits.memory: 1g लिखना एक ऐसी फ़ाइल बनाता है जिसे कोई भी एक नज़र में नहीं पढ़ सकता। यह अनुमान लगाने के बजाय कि कौन सा नंबर प्रभावी रहा, डेमन (daemon) से पूछें:

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

मेमोरी मान बाइट्स में होते हैं, इसलिए 1g को 1073741824 के रूप में प्रिंट किया जाता है। CPU नैनो CPU में होता है, इसलिए 1.5 को 1500000000 के रूप में प्रिंट किया जाता है। किसी भी फ़ील्ड में 0 का अर्थ है कि कोई सीमा निर्धारित नहीं की गई थी। Docker द्वारा स्वीकार की जाने वाली सबसे छोटी मेमोरी सीमा 6m है, और इससे कम होने पर कंटेनर शुरू होने से इनकार कर देता है।

जब कोई कंटेनर अपनी सीमा तक पहुँच जाता है तो क्या होता है

कंटेनर की गति धीमी नहीं होती है। वह बंद हो जाता है।

जब कोई प्रोसेस किसी पेज की मांग करती है और cgroup पहले से ही अपनी memory.max पर होता है, तो कर्नेल सबसे पहले उस cgroup के भीतर जो कुछ भी संभव हो उसे पुनः प्राप्त (reclaim) करता है: क्लीन पेज कैश, और फिर वे पेज जिन्हें स्वैप किया जा सकता है। यदि पुनः प्राप्ति से पर्याप्त मेमोरी खाली नहीं होती है, तो cgroup OOM (आउट ऑफ मेमोरी) किलर कंटेनर के भीतर एक प्रोसेस को चुनता है और उसे SIGKILL भेजता है। कंटेनर के PID 1 को समाप्त करने से कंटेनर बंद हो जाता है। एग्जिट कोड 137 केवल 128 प्लस सिग्नल 9 है, इसलिए 137 किसी भी SIGKILL का फिंगरप्रिंट है, न कि अपने आप में OOM का प्रमाण।

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

true 137 एक OOM किल है। false 137 का अर्थ है कि किसी अन्य चीज़ ने SIGKILL भेजा है, और इसका सामान्य कारण docker compose stop का दस सेकंड की ग्रेस अवधि तक पहुँचना है क्योंकि ऐप ने SIGTERM को अनदेखा कर दिया था। यह अंतर घंटों का समय बचाता है, क्योंकि इन दोनों समस्याओं में कुछ भी समान नहीं है।

दो अन्य स्थान इस घटना को रिकॉर्ड करते हैं। डेमन को लाइव देखें:

docker events --filter event=oom

फिर कर्नेल लॉग पढ़ें, जो वह रिकॉर्ड है जो रीस्टार्ट के बाद भी बना रहता है:

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

एक cgroup किल Memory cgroup out of memory: Killed process 24713 (node) से शुरू होने वाली एक लाइन प्रिंट करता है। Memory cgroup प्रीफिक्स के बिना वाली लाइन एक होस्ट OOM है, जिसका अर्थ है कि मशीन की अपनी RAM समाप्त हो गई है। यह वह विफलता है जिसे रोकने के लिए सीमाएं बनाई गई हैं, इसलिए इसे देखना इस बात का संकेत है कि आपकी सीमाओं का योग बहुत अधिक है, या कुछ सेवाओं की कोई सीमा ही नहीं है।

restart: unless-stopped के साथ, एक OOM लूप आसानी से छिप जाता है, क्योंकि सेवा मरने के एक सेकंड बाद docker compose ps में ऊपर दिखाई देती है। अपटाइम कॉलम और रीस्टार्ट काउंट की जांच करें, और सीमा को एक हेल्थचेक जो ऐप को अस्वस्थ रिपोर्ट करता है के साथ जोड़ें ताकि बार-बार मरने वाला कंटेनर आपके देखे बिना भी दिखाई दे सके।

Reservation एक संकेत है, limit एक नियम है

reservations.memory (पुराना mem_reservation) एक soft floor है। Docker इसे एक soft limit के रूप में वर्णित करता है जो तब सक्रिय होती है जब daemon होस्ट पर contention या कम मेमोरी का पता लगाता है। यह किसी container को इससे ऊपर जाने से कभी नहीं रोकता है, और यह कभी गारंटी नहीं देता है कि जब container मेमोरी मांगेगा तो वह उपलब्ध होगी। यह केवल kernel को उन containers से मेमोरी वापस लेने के लिए प्रेरित करता है जो अपनी reservation से ऊपर हैं।

इसलिए, एक reservation अपने आप में किसी चीज़ की सुरक्षा नहीं करता है। इसका उपयोग उस service को चिह्नित करने के लिए करें जिसे आप दबाव में प्राथमिकता देना चाहते हैं, और सुरक्षा के लिए limit पर निर्भर रहें। Reservation को limit से नीचे रखें, अन्यथा container शुरू नहीं होगा: Docker Minimum memory limit can not be less than memory reservation limit के साथ config को अस्वीकार कर देता है।

Swap accounting, honestly

अधिकांश VPS इमेज में कोई भी swap फ़ाइल नहीं होती है। swapon --show और free -h चलाएँ। यदि swap का कुल योग शून्य है, तो नीचे दी गई swap से संबंधित कोई भी सेटिंग काम नहीं करेगी, और आपकी मेमोरी सीमा केवल RAM तक सीमित रहेगी।

memswap_limit swap की मात्रा नहीं है। यह मेमोरी और swap का कुल योग है। mem_limit: 1g और memswap_limit: 2g के साथ, कंटेनर को 1GB RAM और 1GB swap मिलता है। दोनों मानों को बराबर सेट करने पर कंटेनर को बिल्कुल भी swap नहीं मिलता है। mem_limit को सेट करने और memswap_limit को अनसेट छोड़ने पर कंटेनर अपनी मेमोरी सीमा के आकार तक swap का उपयोग कर सकता है।

Ubuntu 24.04 और Debian 13 डिफ़ॉल्ट रूप से cgroup v2 का उपयोग करते हैं, जहाँ swap एक अलग काउंटर (memory.swap.max) है और यह बिना किसी अतिरिक्त सेटअप के काम करता है। पुराना संदेश Your kernel does not support swap limit capabilities उन cgroup v1 होस्ट से आता है जो swapaccount=1 के बिना बूट किए गए हैं। उन पर मेमोरी सीमा लागू होती है जबकि swap वाले हिस्से को अनदेखा कर दिया जाता है।

ईमानदार रहें कि swap आपको क्या लाभ देता है। यह OOM kill को धीमा बनाता है, न कि कम संभावित, क्योंकि एक लीक करने वाली प्रक्रिया RAM की तरह ही swap को भी भर देती है। इस बीच, साझा VPS स्टोरेज पर swap का उपयोग करने वाला कंटेनर बॉक्स पर मौजूद अन्य सभी सेवाओं को धीमा कर देता है। किसी भी latency-sensitive कार्य के लिए, बिना swap वाली सही सीमा अधिक तेज़ी से और अधिक अनुमानित तरीके से विफल होती है।

मेमोरी का उपयोग वास्तव में जितना है उससे अधिक क्यों दिखता है

MEM USAGE में docker stats का आंकड़ा पेज कैश को शामिल करता है, इसलिए जो कंटेनर बड़ी फ़ाइलों को पढ़ता है, उसका उपयोग उसकी सीमा तक बढ़ जाता है और वहीं बना रहता है। यह सामान्य है और यह कोई लीक नहीं है, क्योंकि OOM killer के सक्रिय होने से पहले क्लीन कैश को वापस ले लिया जाता है। एक सेल्फ-होस्टेड Jellyfin मीडिया सर्वर जैसी सेवा इसी कारण से स्थायी रूप से अपनी सीमा के करीब दिखाई देगी।

कंटेनर के अंदर से संख्या को कैश और वास्तविक वर्किंग सेट में विभाजित करें:

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

anon एनोनिमस मेमोरी है, वह वर्किंग सेट जिसे हटाया नहीं जा सकता। file पेज कैश है, जिसे हटाया जा सकता है। अपनी सीमा को anon और एक मार्जिन के आधार पर निर्धारित करें, न कि कुल योग के आधार पर। memory.events फ़ाइल इस बहस को पूरी तरह समाप्त कर देती है: शून्य से ऊपर का oom_kill काउंटर यह दर्शाता है कि कर्नल ने इस कंटेनर के शुरू होने के बाद से कुछ न कुछ बंद किया है, और बढ़ता हुआ max काउंटर यह दर्शाता है कि कंटेनर को अभी उसकी सीमा पर रोका जा रहा है। दोनों कमांड के लिए इमेज के अंदर एक शेल और coreutils की आवश्यकता होती है, इसलिए वे distroless या scratch इमेज पर काम नहीं करते हैं।

8GB VPS पर साइजिंग सीमाएं

होस्ट से शुरुआत करें, ऐप्स से नहीं। 8GB VPS पर, kernel, Docker daemon, sshd, journald और अपने स्वयं के लॉगिन शेल के लिए लगभग 1GB छोड़ दें। इससे लगभग 7GB मेमोरी उपलब्ध रहती है, और प्रत्येक कंटेनर की सीमा का योग इससे कम रहना चाहिए। ओवरकमिटिंग तब तक काम करती है जब तक कि दो सेवाएं एक साथ पीक पर न पहुंच जाएं।

8GB बॉक्स पर एक व्यावहारिक विभाजन:

  • Reverse proxy: 128m सीमा। यह एक छोटी प्रक्रिया है, और इतनी सख्त सीमा एक रनअवे कॉन्फ़िगरेशन रीलोड को तुरंत पकड़ लेती है।
  • PostgreSQL: 2g सीमा, जिसमें डेटाबेस कॉन्फ़िगरेशन में shared_buffers को लगभग 512MB पर सेट किया गया है।
  • Application container: 1g सीमा।
  • Background worker: 512m सीमा।
  • Media या file service: 2g सीमा, जिसका अधिकांश हिस्सा पेज कैश होगा।

इन नंबरों को अपने स्टैक में कॉपी न करें। सेवाओं को एक दिन के लिए वास्तविक लोड के तहत चलाएं, docker stats को देखें, प्रति कंटेनर पीक anon मान लें और उसमें लगभग आधी अतिरिक्त जगह (हेडरूम) जोड़ें। बहुत सख्त सीमा निर्धारित करना बिना सीमा के होने से भी बदतर है, क्योंकि यह सामान्य ट्रैफ़िक स्पाइक के दौरान एक स्वस्थ सेवा को समाप्त कर देती है।

एक जाल विशेष उल्लेख के योग्य है। अधिकांश रनटाइम के लिए सीमा अदृश्य होती है जब तक कि आप उन्हें इसके बारे में न बताएं। PostgreSQL खुशी-खुशी shared_buffers और work_mem को अपनी कंटेनर सीमा से आगे बढ़ा देगा और मारा जाएगा। JVM (Java virtual machine) को होस्ट RAM के बजाय cgroup सीमा से अपना हीप आकार देने के लिए -XX:MaxRAMPercentage=75 की आवश्यकता होती है। Node.js को मेगाबाइट में --max-old-space-size की आवश्यकता होती है, जिसे कंटेनर सीमा से नीचे सेट किया जाना चाहिए, अन्यथा इसका गार्बेज कलेक्टर हीप को तब तक बढ़ने देता है जब तक कि kernel हस्तक्षेप न करे। cgroup बातचीत नहीं करता है। यह समाप्त कर देता है।

CPU सीमाएं पूरी तरह से अलग तरह से काम करती हैं

cpus: "1.5" का अर्थ है एक कोर का 150%, जिसे CFS (completely fair scheduler) कोटा के रूप में लागू किया जाता है। कंटेनर को हर 100ms की अवधि में 150ms का CPU समय मिलता है, जो उसके सभी थ्रेड्स में साझा होता है। जब यह समय समाप्त हो जाता है, तो कर्नेल उसे अगली अवधि तक प्रतीक्षा करने के लिए मजबूर करता है।

यह एक महत्वपूर्ण अंतर है। यदि कोई कंटेनर अपनी मेमोरी सीमा से अधिक उपयोग करता है, तो उसे समाप्त (kill) कर दिया जाता है। यदि कोई कंटेनर अपनी CPU सीमा से अधिक उपयोग करता है, तो उसे थ्रॉटल (throttle) कर दिया जाता है और वह धीमी गति से चलता रहता है। इसलिए CPU सीमा को आक्रामक रूप से सेट करना सुरक्षित है, जबकि मेमोरी सीमा के लिए अतिरिक्त जगह (headroom) की आवश्यकता होती है।

cpu_shares एक अलग उपकरण है: यह एक सापेक्ष भार (relative weight) है जो केवल तभी मायने रखता है जब CPU वास्तव में पूरी तरह व्यस्त (saturated) हों। 1024 और 512 के शेयर वाले दो कंटेनर एक व्यस्त कोर को लगभग दो-एक के अनुपात में विभाजित करते हैं, और खाली सर्वर पर किसी पर भी कोई प्रतिबंध नहीं होता है। सेवाओं को उनके महत्व के अनुसार रैंक करने के लिए शेयर्स का उपयोग करें, और जब आपको वास्तविक सीमा (ceiling) की आवश्यकता हो, तो cpus का उपयोग करें। उदाहरण के लिए, रात में चलने वाले ट्रांसकोड जॉब को अपने वेब सर्वर के संसाधनों को खत्म करने से रोकने के लिए इसका उपयोग करें।

FAQ

क्या deploy.resources.limits बिना Docker Swarm के काम करता है?

हाँ। जब आप किसी सिंगल होस्ट पर docker compose up चलाते हैं, तो Compose V2 deploy.resources.limits और deploy.resources.reservations को लागू करता है। इसकी पुष्टि docker inspect --format '{{.HostConfig.Memory}}' <container> से करें, जो बाइट्स में लिमिट प्रिंट करता है और कोई लिमिट लागू न होने पर 0 दिखाता है। deploy के अंदर वे कीज़ (keys) जिन्हें वास्तव में Swarm की आवश्यकता होती है, वे mode, placement, update_config और endpoint_mode हैं।

Docker Compose में एग्जिट कोड 137 का क्या अर्थ है?

इसका अर्थ है कि मुख्य प्रोसेस को SIGKILL प्राप्त हुआ, क्योंकि 137 का मान 128 प्लस सिग्नल 9 होता है। कर्नल OOM किलर इसका सामान्य कारण है, लेकिन जब कोई ऐप SIGTERM को अनदेखा करता है, तो शटडाउन टाइमआउट भी यही कोड उत्पन्न करता है। इनके बीच अंतर करने के लिए docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> चलाएं। true 137 एक मेमोरी किल है, और false 137 नहीं है।

क्या मुझे mem_limit सेट करना चाहिए या deploy.resources.limits.memory?

दोनों ही docker compose के साथ काम करते हैं। deploy.resources.limits.memory वर्तमान Compose Specification का प्रारूप है और नई फ़ाइल के लिए बेहतर डिफ़ॉल्ट है। यदि आपकी फ़ाइल के बाकी हिस्से पहले से ही पुराने टॉप-लेवल कीज़ का उपयोग कर रहे हैं, तो mem_limit को बनाए रखें। एक ही सर्विस पर दोनों को सेट करने से फ़ाइल को पढ़ना कठिन हो जाता है, इसलिए किसी एक को चुनें और docker inspect के साथ परिणाम सत्यापित करें।

मेरा कंटेनर अपनी पूरी मेमोरी लिमिट पर क्यों है, जबकि उसे किल नहीं किया जा रहा है?

docker stats में उपयोग का आंकड़ा पेज कैश को शामिल करता है, जिसे कर्नल OOM किल ट्रिगर करने के बजाय दबाव में हटा देता है। docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat चलाएं और anon मान पढ़ें, जो कि वर्किंग सेट है जिसे पुनः प्राप्त नहीं किया जा सकता है। कम anon मान के साथ उच्च file मान का अर्थ है कि कंटेनर डिस्क इनपुट और आउटपुट कर रहा है, न कि यह कि कंटेनर बंद होने वाला है।

मुझे 8GB VPS पर कितनी RAM अनअलोकेटेड (unallocated) छोड़नी चाहिए?

कर्नल, Docker daemon, sshd, journald और अपने स्वयं के शेल के लिए लगभग 1GB छोड़ दें, फिर सभी कंटेनर लिमिट्स का योग शेष 7GB के भीतर रखें। संख्याएं तय करने से पहले एक दिन वास्तविक लोड के तहत प्रति कंटेनर पीक anon मान की निगरानी करें, और कुल योग को भरने के लक्ष्य के बजाय एक बजट के रूप में मानें।