SSD Nodes Learn Hosting plans →
मार्गदर्शक Matt Connorद्वारे Matt Connor · अपडेटेड 2026-08-29

Docker Compose मध्ये OOM थांबवण्यासाठी memory limit

Docker Compose मध्ये mem_limit आणि deploy.resources वापरून memory व CPU मर्यादा लावा. exit 137, swap आणि VPS साठी योग्य sizing कसे तपासायचे ते जाणून घ्या.

Docker Compose memory limit काय करते

Docker Compose memory limit म्हणजे Linux kernel एका कंटेनरच्या cgroup वर लावलेली कठोर कमाल मर्यादा. cgroup म्हणजे प्रक्रियांच्या समूहासाठी संसाधनांचे मापन करणारे kernel feature आहे. सेवेसाठी deploy.resources.limits.memory सेट केल्यावर त्या कंटेनरला तुम्ही लिहिलेल्या मूल्यापेक्षा जास्त memory वापरता येत नाही. कंटेनरने ही मर्यादा ओलांडण्याचा प्रयत्न केल्यास kernel कंटेनरमधील एखादी process बंद करते. कंटेनर सामान्यतः 137 code सह बंद होतो.

VPS मध्ये याचे महत्त्व सर्वाधिक असते. VPS मधील RAM निश्चित असते आणि वापरण्यासाठी अतिरिक्त host memory उपलब्ध नसते. memory leak किंवा चुकीची query असलेला एक कंटेनर 8GB मशीनवरील प्रत्येक मोकळा page वापरू शकतो. त्यानंतर kernel च्या मते सर्वाधिक समस्याप्रवण असलेली process बंद केली जाते. ती process समस्या निर्माण करणाऱ्या कंटेनरमधील process नसून database किंवा तुमचे SSH session असू शकते. Limits मुळे संपूर्ण सर्व्हर बंद पडण्याऐवजी केवळ एक सेवा restart होते.

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 column मध्ये 142MiB / 1GiB सारखे मूल्य दिसले पाहिजे. Limit column मध्ये संपूर्ण host RAM दिसत असल्यास setting लागू झालेली नाही. ती लागू होईपर्यंत या मार्गदर्शकातील पुढील सूचना उपयोगी ठरणार नाहीत. compose file तुमच्यासाठी नवीन असल्यास, VPS साठी Docker Compose ची मूलभूत माहिती या file layout चे स्पष्टीकरण देते.

deploy.resources.limits किंवा mem_limit: कोणते लागू होते

या दोन्ही संज्ञा एकाच संकल्पनेसाठी वापरल्या जातात. त्यामुळे गोंधळ होतो.

mem_limit, mem_reservation, memswap_limit, cpus आणि cpu_shares या जुन्या Compose file formats मधून आलेल्या top-level service keys आहेत. deploy.resources हा Swarm schema मधून आला असून आता Compose Specification चा भाग आहे. आज docker compose हाच format वाचते.

एकाच host वर दोन्ही कार्य करतात. Swarm cluster नसतानाही Compose V2, म्हणजे docker compose plugin, docker compose up चालवल्यावर deploy.resources.limits आणि deploy.resources.reservations लागू करते. deploy block मधील Swarm-पुरते भाग म्हणजे इतर keys आहेत: mode, placement, update_config आणि endpoint_mode यांचा अर्थ docker stack deploy साठी असतो आणि docker compose up त्यांच्याकडे दुर्लक्ष करते. त्यामुळे "deploy साठी Swarm आवश्यक आहे" हा सर्वसाधारण सल्ला C resources subsection साठी चुकीचा आहे. तो पाळल्यास तुमच्या services वर कोणतीही मर्यादा लागू होत नाही.

प्रत्येक project साठी एकच संज्ञा वापरा. एकाच 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 असल्यास कोणतीही मर्यादा सेट केलेली नाही. Docker स्वीकारत असलेली सर्वात कमी memory limit 6m आहे. त्यापेक्षा कमी value दिल्यास container सुरू होत नाही.

मर्यादेपर्यंत पोहोचल्यावर container मध्ये काय होते

container मंद होत नाही. तो बंद पडतो.

एखाद्या process ने page मागितली आणि cgroup आधीच त्याच्या memory.max वर असेल, तर kernel प्रथम त्या cgroup मध्ये शक्य तेवढी memory reclaim करतो: आधी clean page cache, त्यानंतर swap करता येणारी pages. Reclaim करूनही पुरेशी memory मोकळी झाली नाही, तर 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 पाठवला. याचे नेहमीचे कारण म्हणजे app ने SIGTERM कडे दुर्लक्ष केल्यामुळे docker compose stop ची दहा सेकंदांची grace period संपणे. हा फरक समजल्याने अनेक तास वाचतात, कारण या दोन समस्यांचा परस्पर संबंध नाही.

ही घटना आणखी दोन ठिकाणी नोंदवली जाते. daemon चे live निरीक्षण करा:

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 दर्शवते. याचा अर्थ संपूर्ण मशीनची RAM संपली. हीच परिस्थिती टाळण्यासाठी limits असतात. त्यामुळे अशी ओळ दिसल्यास तुमच्या limits ची एकूण बेरीज खूप जास्त आहे किंवा काही services वर कोणतीही limit लागू केलेली नाही, असे समजा.

restart: unless-stopped वापरल्यास OOM loop सहज लक्षात येत नाही, कारण service बंद पडल्यानंतर एका सेकंदाने ती docker compose ps मध्ये up दिसते. uptime column आणि restart count तपासा. तसेच limit सोबत app ला unhealthy म्हणून नोंदवणारा healthcheck जोडा, म्हणजे सतत बंद पडणारा container तुम्ही स्वतः निरीक्षण करत नसतानाही दिसून येईल.

Reservation हा संकेत आहे; limit हा नियम आहे

reservations.memory (जुने mem_reservation) हे soft floor आहे. Docker त्याचे वर्णन soft limit म्हणून करते. Host वर contention किंवा memory कमी असल्याचे daemon ला आढळल्यावर ते सक्रिय होते. यामुळे container ला त्या मर्यादेपेक्षा जास्त memory वापरण्यापासून कधीही रोखले जात नाही. तसेच container ने memory मागितल्यावर ती मोकळी असेल याची हमीही मिळत नाही. हे फक्त kernel ला आधी reservation पेक्षा जास्त वापर करणाऱ्या containers मधून memory reclaim करण्यास प्रवृत्त करते.

म्हणून reservation स्वतःहून कशाचेही संरक्षण करत नाही. दबावाच्या परिस्थितीत ज्या service ला अनुकूल वागणूक मिळावी असे तुम्हाला वाटते, ती दर्शवण्यासाठी reservation वापरा आणि सुरक्षिततेसाठी limit वर अवलंबून रहा. Reservation ही limit पेक्षा कमी ठेवा. अन्यथा container सुरू होणार नाही: Docker Minimum memory limit can not be less than memory reservation limit सह configuration नाकारते.

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 सेट करून 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 हा swapaccount=1 शिवाय boot झालेल्या cgroup v1 hosts कडून येतो. अशा hosts वर memory limit लागू राहते, पण swap भाग दुर्लक्षित केला जातो.

swap मुळे प्रत्यक्षात काय मिळते याबाबत स्पष्ट रहा. त्यामुळे OOM kill होण्यास अधिक वेळ लागतो; मात्र त्याची शक्यता कमी होत नाही. कारण memory leak असलेली process RAM प्रमाणेच swap देखील भरते. दरम्यान, shared VPS storage वर swap मुळे सतत I/O करणारा container त्या मशीनवरील इतर प्रत्येक सेवेचा वेग कमी करतो. कमी latency आवश्यक असलेल्या कोणत्याही सेवेसाठी swap शिवाय योग्य limit ठेवणे अधिक जलद आणि अधिक अंदाज करता येण्याजोगे अपयश देते.

प्रत्यक्ष वापरापेक्षा memory usage अधिक दिसण्याचे कारण

MEM USAGE मधील आकड्यात docker stats समाविष्ट असतो. त्यामुळे मोठ्या files वाचणारा container आपल्या limit पर्यंत memory usage वाढवतो आणि तिथेच राहतो. हे सामान्य आहे. हा leak नाही, कारण OOM killer कॉल होण्यापूर्वी clean cache reclaim केला जातो. 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 आहे. तो reclaim करता येतो. तुमची limit एकूण memory usageवर नव्हे, तर anon अधिक काही margin यावर आधारित ठेवा. memory.events file याचा स्पष्ट पुरावा देते: oom_kill counter शून्यापेक्षा मोठा असल्यास, container सुरू झाल्यापासून kernelने त्यातील काहीतरी process kill केले आहे. max counter वाढत असल्यास, container सध्या आपल्या कमाल मर्यादेवर अडकलेला आहे. दोन्ही commandsसाठी imageमध्ये shell आणि coreutils आवश्यक आहेत. त्यामुळे distroless किंवा scratch imageवर त्या अपयशी ठरतात.

8GB VPS वरील आकारमान मर्यादा

अॅप्सपासून सुरुवात करू नका. आधी host पासून सुरुवात करा. 8GB VPS वर kernel, Docker daemon, sshd, journald आणि तुमच्या login shell साठी सुमारे 1GB राखून ठेवा. त्यामुळे वापरासाठी सुमारे 7GB उरतात. प्रत्येक container च्या मर्यादांची एकूण बेरीज या मर्यादेपेक्षा कमी राहिली पाहिजे. दोन सेवा एकाच वेळी कमाल संसाधने वापरेपर्यंत overcommitting चालते.

8GB मशीनसाठी वापरता येणारी विभागणी:

  • Reverse proxy: 128m limit. ही लहान process असते. इतकी कडक limit runaway config reload त्वरित पकडते.
  • PostgreSQL: 2g limit; database config मध्ये shared_buffers सुमारे 512MB वर सेट करा.
  • Application container: 1g limit.
  • Background worker: 512m limit.
  • Media किंवा file service: 2g limit; यातील बहुतांश भाग page cache साठी वापरला जाईल.

ही आकडेवारी तुमच्या स्वतःच्या stack मध्ये तशीच वापरू नका. सेवा प्रत्यक्ष load अंतर्गत एक दिवस चालवा, docker stats monitor करा, प्रत्येक container साठी कमाल anon मूल्य नोंदवा आणि त्यावर headroom म्हणून साधारण निम्मी अतिरिक्त क्षमता जोडा. खूप कमी limit ठेवणे limit न ठेवण्यापेक्षा धोकादायक असते, कारण सामान्य traffic spike दरम्यानही निरोगी सेवा बंद होऊ शकते.

एका समस्येकडे स्वतंत्रपणे लक्ष द्या. Runtime ला limit सांगितली नाही, तर ती बहुतेक runtimes साठी अदृश्य राहते. PostgreSQL shared_buffers आणि work_mem यांचा आकार container limit पेक्षा जास्त ठेवू शकते आणि तिची process kill होऊ शकते. JVM (Java virtual machine) ला host RAM ऐवजी cgroup limit वरून heap चा आकार ठरवण्यासाठी -XX:MaxRAMPercentage=75 आवश्यक आहे. Node.js मध्ये --max-old-space-size megabytes मध्ये, container limit पेक्षा कमी मूल्यावर सेट करा. अन्यथा त्याचा garbage collector heap वाढू देतो आणि शेवटी kernel हस्तक्षेप करतो. Ollama मध्येही हीच समस्या आहे, मात्र setting वेगळी आहे, कारण num_ctx वाढवल्याने KV cache चा आकार वाढतो आणि काहीशे megabytes वाढल्यानंतर दीर्घ prompt पूर्ण होण्यापूर्वीच container बंद होतो. cgroup वाटाघाटी करत नाही. ते process kill करते.

CPU limits पूर्णपणे वेगळ्या प्रकारे कार्य करतात

cpus: "1.5" म्हणजे एका core च्या 150% इतका CPU वेळ. तो CFS (completely fair scheduler) quota म्हणून लागू केला जातो. कंटेनरला प्रत्येक 100ms कालावधीत 150ms CPU वेळ मिळतो आणि हा वेळ त्याच्या सर्व threads मध्ये सामायिक केला जातो. हा वेळ संपल्यानंतर kernel पुढील कालावधीपर्यंत त्याला प्रतीक्षा करण्यास भाग पाडतो.

हा महत्त्वाचा फरक आहे. Memory limit ओलांडणारा कंटेनर बंद केला जातो. CPU limit ओलांडणाऱ्या कंटेनरला throttling लागू होते आणि तो कमी वेगाने पुढे कार्यरत राहतो. त्यामुळे CPU limit तुलनेने आक्रमकपणे सेट करणे सुरक्षित असते. मात्र memory limit साठी अतिरिक्त headroom आवश्यक असतो.

cpu_shares हे वेगळे साधन आहे. हे relative weight असते आणि CPUs प्रत्यक्षात पूर्ण क्षमतेने वापरले जात असतील तेव्हाच त्याचा परिणाम होतो. 1024 आणि 512 shares असलेले दोन कंटेनर व्यस्त core साधारणपणे दोन-एक या प्रमाणात वाटून घेतात. Idle box वर मात्र त्यांपैकी कोणत्याही कंटेनरवर मर्यादा येत नाही. सेवांचे महत्त्वानुसार क्रम ठरवण्यासाठी shares वापरा. प्रत्यक्ष कमाल मर्यादा आवश्यक असल्यास cpus वापरा. उदाहरणार्थ, nightly transcode job मुळे तुमच्या web server ला पुरेसे CPU न मिळणे थांबवण्यासाठी ते वापरता येते.

FAQ

deploy.resources.limits Docker Swarm शिवाय कार्य करते का?

होय. एका host वर `docker compose up चालवताना Compose V2 deploy.resources.limits आणि deploy.resources.reservations लागू करते. docker inspect --format '{{.HostConfig.Memory}}' <container> वापरून याची खात्री करा. हा command limit bytes मध्ये दाखवतो आणि कोणतीही limit लागू नसेल तर 0 दाखवतो. deploy मधील Swarm आवश्यक असलेल्या keys म्हणजे 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 सोबत दोन्हीपैकी कोणतेही वापरता येते. deploy.resources.limits.memory हा सध्याच्या Compose Specification मधील प्रकार आहे आणि नवीन file साठी तो अधिक योग्य default आहे. तुमच्या file च्या उर्वरित भागात जुने top-level keys आधीपासून वापरले असतील, तर mem_limit तसेच ठेवा. एका service वर दोन्ही सेट केल्याने file वाचणे अधिक कठीण होते. त्यामुळे एकच प्रकार निवडा आणि docker inspect` वापरून परिणाम पडताळा.

माझा container kill न होता त्याच्या पूर्ण memory limit इतकी memory का वापरत आहे?

`docker stats मधील usage figure मध्ये page cache समाविष्ट असतो. Memory pressure आल्यावर kernel OOM kill सुरू करण्याऐवजी हा cache काढून टाकतो. docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat चालवा आणि anon value वाचा. ही value reclaim करता न येणारा working set दर्शवते. कमी anon value सोबत उच्च file` value दिसत असल्यास container disk input आणि output करत आहे; तो बंद पडण्याच्या स्थितीत नाही.

8GB VPS वर किती RAM unallocated ठेवावी?

Kernel, Docker daemon, sshd, journald आणि तुमच्या shell साठी सुमारे 1GB ठेवा. त्यानंतर सर्व container limits ची बेरीज उरलेल्या 7GB पेक्षा कमी ठेवा. प्रत्यक्ष load असताना प्रत्येक container ची peak `anon` value एका दिवसासाठी monitor करा आणि त्यानंतरच numbers निश्चित करा. एकूण memory ला भरून काढायचे target न मानता budget म्हणून वापरा.