Memory and CPU Limits

Limiting Memory and CPU for a Container

Resource Limits showed --memory and --cpus becoming cgroup files. Two details matter in practice. --memory alone lets the container swap as much again (--memory-swap defaults to twice the limit), and WSL2 6 has 8 GB of swap. And Node.js 2,131 does not size its heap to the limit. Test both with BookNest's image and a loop that allocates 1 MB buffers:

Swap doubles the limit, and V8 sizes its heap past itShell
EAT='const a=[]; for (let i=0;;i++) {a.push(Buffer.alloc(1e6,1)); if (i%50==0) console.log(i)}'
I=l3-booknest-api:latest
docker run --name l3-oom --memory 64m $I node -e "$EAT" | tail -1
docker inspect -f '{{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}' l3-oom
docker rm l3-oom >/dev/null
docker run --rm --memory 64m --memory-swap 64m $I node -e "$EAT" | tail -1
HEAP='require("v8").getHeapStatistics().heap_size_limit >> 20'
for m in 128m 2g; do
  echo "$m limit: heap $(docker run --rm --memory $m $I node -p "$HEAP") MiB"; done
Output
100
137 OOMKilled=true
50
128m limit: heap 259 MiB
2g limit: heap 1120 MiB

With swap, the first container passed 100 MB under a 64 MB limit before the kernel's OOM killer ended it with SIGKILL (137 = 128 + 9); with --memory-swap equal to --memory it died before 100 MB. The heap figures are the surprise: under a 128 MB limit V8 86,723 still plans a 259 MiB heap, so a leaking API never reaches Node's own "heap out of memory" error and stack trace; the kernel kills it silently first. Set the heap below the limit with --max-old-space-size (in MiB), leaving room for buffers and native code, and give the Nginx 75 container a ceiling:

compose.prod.yaml: a heap that fits the limit, and a ceiling for webShell
services:
  web:
    deploy: { resources: { limits: { cpus: "0.5", memory: 64M } } }
  api:
    environment:
      NODE_OPTIONS: --max-old-space-size=192

--cpus 1.0 is a quota of one CPU's time per 100 ms period, over any cores; --cpuset-cpus 0,1 pins cores.