SSD Nodes Learn 8GB RAM — $66/taon
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-02

Docker Compose memory limit para maiwasan ang OOM

Itakda ang memory at CPU limits sa Docker Compose para hindi maubos ng isang container ang VPS. Alamin ang deploy.resources, mem_limit, exit 137, swap, at sizing.

Ano ang ginagawa ng Docker Compose memory limit

Ang Docker Compose memory limit ay mahigpit na limitasyong itinatakda ng Linux kernel sa cgroup (control group, isang feature ng kernel na sumusukat sa resource usage ng isang pangkat ng mga proseso) ng isang container. Itakda ang deploy.resources.limits.memory sa isang service, at hindi kailanman lalampas ang container na iyon sa itinakdang halaga. Kapag sinubukan nitong lumampas, papatayin ng kernel ang isang proseso sa loob ng container, at karaniwang lalabas ang container na may code 137.

Pinakamahalaga ito sa isang VPS, kung saan fixed ang RAM at walang ekstrang memorya sa host na maaaring gamitin. Ang isang container na may memory leak o maling query ay maaaring gumamit ng bawat libreng page sa isang 8GB na server. Pagkatapos, papatayin ng kernel ang prosesong itinuturing nitong pinakamalaking problema. Kadalasan, database o SSH session ang napapatay sa halip na ang container na sanhi ng problema. Ginagawang isang service na maaaring mag-restart ng mga limit ang buong-server outage.

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

Ilapat ito at kumpirmahing aktibo ang limit:

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

Dapat magpakita ang column na MEM USAGE / LIMIT ng halagang tulad ng 142MiB / 1GiB. Kung ipinapakita ng limit column ang buong RAM ng host, hindi nailapat ang setting. Hindi makatutulong ang natitirang bahagi ng gabay na ito hangga't hindi ito naayos. Kung bago sa iyo ang compose file mismo, tinatalakay sa mga pangunahing kaalaman sa Docker Compose para sa VPS ang file layout na ginagamit dito.

deploy.resources.limits o mem_limit: alin ang nalalapat

May dalawang spelling para sa parehong ideya, kaya nakalilito ito.

Ang mem_limit, mem_reservation, memswap_limit, cpus at cpu_shares ay mga top-level service key na minana mula sa mas lumang mga format ng Compose file. Ang deploy.resources ay nagmula sa Swarm schema at bahagi na ngayon ng Compose Specification, na siyang format na binabasa ng docker compose sa kasalukuyan.

Parehong gumagana ang mga ito sa iisang host. Inilalapat ng Compose V2, ang docker compose plugin, ang deploy.resources.limits at deploy.resources.reservations kapag pinatakbo mo ang docker compose up, kahit walang Swarm cluster. Ang mga bahagi ng deploy block na para lang sa Swarm ay ang iba pang mga key: ang mode, placement, update_config at endpoint_mode ay may kahulugan sa docker stack deploy at binabalewala ng docker compose up. Kaya mali ang karaniwang payo na “kailangan ng Swarm ang deploy” para sa resources subsection. Kapag sinunod ito, mawawalan ng limit ang iyong mga service.

Pumili ng isang spelling para sa bawat project. Kapag isinulat mo ang mem_limit: 512m at deploy.resources.limits.memory: 1g sa parehong service, magiging mahirap basahin agad ang file. Sa halip na hulaan kung aling numero ang ginamit, tanungin ang daemon:

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

Ang mga memory value ay nasa bytes, kaya ipinapakita ng 1g ang 1073741824. Ang CPU ay nasa nano CPUs, kaya ipinapakita ng 1.5 ang 1500000000. Ang 0 sa anumang field ay nangangahulugang walang itinakdang limit. Ang pinakamaliit na memory limit na tinatanggap ng Docker ay 6m. Kapag mas mababa rito, hindi magsisimula ang container.

Ano ang nangyayari kapag naabot ng container ang limit

Hindi bumabagal ang container. Tumitigil ito.

Kapag humingi ng page ang isang process at nasa memory.max na ang cgroup, unang binabawi ng kernel ang maaari nitong mabawi sa loob ng cgroup na iyon: ang malinis na page cache, at pagkatapos ay ang mga page na maaari nitong i-swap. Kung hindi sapat ang mabawi, pipili ang cgroup OOM (out of memory) killer ng process sa loob ng container at padadalhan ito ng SIGKILL. Kapag pinatay ang PID 1 ng container, matatapos ang container. Ang exit code 137 ay 128 dagdag sa signal 9, kaya fingerprint ang 137 ng anumang SIGKILL, pero hindi ito patunay na OOM ang sanhi.

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

Ang true 137 ay isang OOM kill. Ang false 137 ay nangangahulugang ibang proseso ang nagpadala ng SIGKILL, at ang karaniwang sanhi ay lumampas ang docker compose stop sa ten second grace period nito dahil hindi pinansin ng app ang SIGTERM. Mahalaga ang pagkakaibang ito dahil walang kaugnayan ang dalawang problemang ito.

May dalawa pang lugar na nagtatala ng event. I-monitor nang live ang daemon:

docker events --filter event=oom

Pagkatapos, basahin ang kernel log. Ito ang record na nananatili kahit mag-restart:

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

Ang cgroup kill ay nagpi-print ng linyang nagsisimula sa Memory cgroup out of memory: Killed process 24713 (node). Ang linyang walang prefix na Memory cgroup ay isang host OOM, na nangangahulugang naubusan mismo ng RAM ang machine. Ito ang failure na dapat pigilan ng mga limit. Kapag nakita mo ito, senyales ito na masyadong mataas ang kabuuan ng iyong mga limit, o may ilang service na walang limitasyon.

Sa restart: unless-stopped, madaling hindi mapansin ang OOM loop dahil lumalabas na up ang service sa docker compose ps isang segundo matapos itong mamatay. Suriin ang uptime column at ang restart count. Itambal ang limit sa healthcheck na nag-uulat na unhealthy ang app upang makita ang container na patuloy na namamatay kahit hindi mo ito mino-monitor.

Pahiwatig ang reservation; ang limit ang tuntunin

Ang reservations.memory (ang mas lumang mem_reservation) ay isang soft floor. Inilalarawan ito ng Docker bilang soft limit na ina-activate kapag may contention o mababa ang memory sa host na natukoy ng daemon. Hindi nito kailanman pinipigilan ang container na lumampas dito, at hindi rin nito ginagarantiya na magiging available ang memory kapag humingi ang container. Tinutulungan lamang nitong unahin ng kernel ang pag-reclaim mula sa mga container na lampas sa kanilang reservation.

Kaya walang pinoprotektahan ang reservation kapag mag-isa itong ginagamit. Gamitin ito upang tukuyin ang service na nais mong tratuhin nang mas maayos kapag may pressure, at umasa sa limit para sa kaligtasan. Panatilihing mas mababa ang reservation kaysa sa limit; kung hindi, hindi magsisimula ang container dahil tatanggihan ng Docker ang config gamit ang Minimum memory limit can not be less than memory reservation limit.

Tumpak na accounting ng swap

Karamihan sa VPS image ay inilalabas na walang swap file. Patakbuhin ang swapon --show at free -h. Kung zero ang kabuuang swap, walang epekto ang lahat ng setting na may kaugnayan sa swap sa ibaba, at purong RAM cap ang iyong memory limit.

Ang memswap_limit ay hindi ang dami ng swap. Ito ang kabuuan ng memory at swap. Sa mem_limit: 1g at memswap_limit: 2g, magkakaroon ang container ng 1GB RAM at 1GB swap. Kapag itinakda na magkapantay ang dalawang value, walang swap ang container. Kapag itinakda ang mem_limit at iniwang hindi nakatakda ang memswap_limit, maaari muling gumamit ang container ng swap hanggang sa laki ng memory limit nito.

Ginagamit ng Ubuntu 24.04 at Debian 13 ang cgroup v2 bilang default. Dito, hiwalay na counter ang swap (memory.swap.max), at gumagana ito nang walang karagdagang setup. Ang lumang mensaheng Your kernel does not support swap limit capabilities ay nagmumula sa mga cgroup v1 host na nag-boot nang walang swapaccount=1. Sa mga host na iyon, ipinapatupad pa rin ang memory limit habang binabalewala ang bahagi para sa swap.

Maging tumpak sa sinasabi mong pakinabang ng swap. Pinapabagal nito ang OOM kill, ngunit hindi nito binabawasan ang posibilidad nito, dahil mabilis punuin ng memory leak ang swap gaya ng pagpuno nito sa RAM. Samantala, ang container na patuloy na gumagamit ng swap sa shared VPS storage ay nagpapabagal sa lahat ng iba pang service sa server. Para sa anumang service na sensitibo sa latency, mas mabilis at mas predictable ang failure kapag tama ang limit at walang swap.

Bakit mas mataas ang mukhang memory usage kaysa sa aktuwal

Kasama sa figure na MEM USAGE sa docker stats ang page cache, kaya ang container na nagbabasa ng malalaking file ay tataas ang usage hanggang malapit sa limit nito at mananatili roon. Normal ito, at hindi ito memory leak, dahil nire-reclaim ang malinis na cache bago pa man tawagin ang OOM killer. Ang service gaya ng self-hosted na Jellyfin media server ay magmumukhang permanenteng malapit sa ceiling nito dahil mismo sa kadahilanang ito.

Hatiin ang value sa cache at aktuwal na working set mula sa loob ng container:

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

Ang anon ay anonymous memory, ang working set na hindi maaaring i-drop. Ang file ay page cache, na maaaring i-reclaim. Itakda ang limit batay sa anon at dagdag na margin, hindi sa kabuuang value. Tiyak na makikita sa file na memory.events ang sagot: ang oom_kill counter na higit sa zero ay nangangahulugang may pinatay ang kernel sa container na ito mula nang magsimula ito, at ang tumataas na max counter ay nangangahulugang kasalukuyang pinapanatili ang container sa ceiling nito. Parehong nangangailangan ang mga command ng shell at coreutils sa loob ng image, kaya mabibigo ang mga ito sa distroless o scratch image.

Mga limitasyon sa sizing sa isang 8GB VPS

Magsimula sa host, hindi sa mga app. Sa isang 8GB VPS, maglaan ng humigit-kumulang 1GB para sa kernel, Docker daemon, sshd, journald, at sarili mong login shell. Nagtitira ito ng humigit-kumulang 7GB na maaaring ilaan, at dapat manatiling mas mababa rito ang kabuuan ng limitasyon ng bawat container. Gumagana ang overcommit hanggang sa araw na sabay mag-peak ang dalawang service.

Isang praktikal na hatian para sa isang 8GB server:

  • Reverse proxy: 128m na limitasyon. Maliit itong process, at agad na natutukoy ng ganitong kasikip na limitasyon ang runaway config reload.
  • PostgreSQL: 2g na limitasyon, na may shared_buffers na nakatakda sa humigit-kumulang 512MB sa database config.
  • Application container: 1g na limitasyon.
  • Background worker: 512m na limitasyon.
  • Media o file service: 2g na limitasyon, na karamihan ay gagamitin bilang page cache.

Huwag mong kopyahin ang mga numerong ito sa sarili mong stack. Patakbuhin ang mga service sa ilalim ng totoong load sa loob ng isang araw, i-monitor ang docker stats, kunin ang peak na anon value ng bawat container, at magdagdag ng humigit-kumulang kalahati nito bilang headroom. Mas masama ang sobrang sikip na limitasyon kaysa walang limitasyon, dahil pinapatay nito ang maayos na service habang may normal na traffic spike.

May isang karaniwang problema na nangangailangan ng hiwalay na paliwanag. Hindi nakikita ng karamihan ng runtime ang limitasyon maliban kung ipapaalam mo ito sa kanila. Masusunog ng PostgreSQL ang shared_buffers at work_mem lampas sa limitasyon ng container nito at papatayin ito. Kailangan ng JVM (Java virtual machine) ang -XX:MaxRAMPercentage=75 upang i-size ang heap batay sa limitasyon ng cgroup, hindi sa RAM ng host. Kailangan ng Node.js ang --max-old-space-size sa megabytes, na nakatakda sa mas mababang halaga kaysa sa limitasyon ng container; kung hindi, palalakihin ng garbage collector ang heap hanggang mamagitan ang kernel. Hindi nakikipagkasundo ang cgroup. Pinapatay nito.

Lubhang Naiiba ang Pag-uugali ng mga CPU Limit

Ang cpus: "1.5" ay nangangahulugang 150% ng isang core, na ipinapatupad bilang CFS (completely fair scheduler) quota. Nakakakuha ang container ng 150ms na CPU time sa bawat 100ms na period, na pinaghahatian ng lahat ng thread nito. Kapag nagamit na nito ang oras na iyon, pinaghihintay ito ng kernel hanggang sa susunod na period.

Iyan ang mahalagang pagkakaiba. Kapag lumampas ang isang container sa memory limit nito, kini-kill ito. Kapag lumampas ito sa CPU limit nito, tina-throttle ito at nagpapatuloy, ngunit mas mabagal. Kaya ligtas magtakda ng mataas na CPU limit, samantalang kailangan ng headroom ang memory limit.

Ibang tool ang cpu_shares: isa itong relative weight na may epekto lamang kapag talagang saturated ang mga CPU. Ang dalawang container na may shares na 1024 at 512 ay maghahati sa isang abalang core nang humigit-kumulang two-to-one, at kapag idle ang machine, hindi nalilimitahan ang alinman sa mga ito. Gamitin ang shares upang isaayos ang mga serbisyo ayon sa kahalagahan, at gamitin ang cpus kapag kailangan mo ng aktuwal na ceiling, halimbawa upang pigilan ang isang nightly transcode job na maubusan ng CPU ang iyong web server.

FAQ

Gumagana ba ang deploy.resources.limits nang walang Docker Swarm?

Oo. Inia-apply ng Compose V2 ang deploy.resources.limits at deploy.resources.reservations kapag pinatakbo mo ang docker compose up sa iisang host. Kumpirmahin ito gamit ang docker inspect --format '{{.HostConfig.Memory}}' <container>, na nagpi-print ng limit sa bytes at nagpi-print ng 0 kapag walang na-apply na limit. Ang mga key sa loob ng deploy na talagang nangangailangan ng Swarm ay mode, placement, update_config at endpoint_mode.

Ano ang ibig sabihin ng exit code 137 sa Docker Compose?

Ibig sabihin, nakatanggap ang pangunahing proseso ng SIGKILL, dahil ang 137 ay 128 dagdag ang signal 9. Karaniwang sanhi ang kernel OOM killer, ngunit nagbubunga rin ng parehong code ang timeout sa pag-shutdown kapag hindi pinapansin ng app ang SIGTERM. Patakbuhin ang docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> upang matukoy ang pagkakaiba. Ang true 137 ay memory kill, samantalang hindi ganoon ang false 137.

Dapat ko bang itakda ang mem_limit o deploy.resources.limits.memory?

Pareho itong gumagana sa docker compose. Ang deploy.resources.limits.memory ang kasalukuyang anyo sa Compose Specification at mas mainam na default para sa bagong file. Panatilihin ang mem_limit kung ginagamit na ng ibang bahagi ng file mo ang mas lumang top-level keys. Ang pagtatakda ng pareho sa isang service ay nagpapahirap lang basahin ang file, kaya pumili ng isa at beripikahin ang resulta gamit ang docker inspect.

Bakit nasa buong memory limit nito ang container ko pero hindi ito napatitigil?

Kasama sa usage figure sa docker stats ang page cache, na inaalis ng kernel kapag may memory pressure sa halip na mag-trigger ng OOM kill. Patakbuhin ang docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat at basahin ang value ng anon, na tumutukoy sa working set na hindi maaaring i-reclaim. Ang mataas na value ng file kasabay ng mababang value ng anon ay nangangahulugang nagsasagawa ang container ng disk input at output, hindi na malapit na itong tumigil.

Gaano karaming RAM ang dapat kong iwanang hindi inilalaan sa isang 8GB VPS?

Maglaan ng humigit-kumulang 1GB para sa kernel, Docker daemon, sshd, journald at sarili mong shell, pagkatapos ay panatilihing mas mababa sa natitirang 7GB ang kabuuan ng lahat ng container limits. Subaybayan ang peak anon value ng bawat container sa aktuwal na load sa loob ng isang araw bago magtakda ng mga numero, at ituring ang kabuuan bilang budget, hindi bilang target na kailangang punuin.