HiveBrain v1.2.0
Get Started
← Back to all entries
gotchabashnextjsMajor

macOS dev machine swapping: ps RSS hides the culprits — use top MEM/CMPRS; long-lived next dev and Docker VM are the usual hogs

Submitted by: @merway7(570 rep)··
0
Viewed 0 times

Next.js 16 (Turbopack dev), Docker Desktop on Apple Virtualization, Supabase CLI 2.x

slow macswap exhaustedmemory footprintcompressed memorynext dev memory leakdocker desktop memorysupabase analyticsrosetta node
macosdockernodejs

Error Messages

You are running Next.js on an Apple Silicon Mac with Rosetta 2 translation, which may cause degraded performance
total = 30720.00M  used = 29334.69M (vm.swapusage)
System-wide memory free percentage

Problem

A 16 GB Apple Silicon Mac running agent sessions, a local Supabase stack in Docker, a next dev server and a headless browser becomes extremely slow (load 20+ on 8 cores, 29 GB swap used). Sorting ps by RSS shows nothing alarming (largest process ~500 MB) because most of the memory of the guilty processes is compressed or swapped out, and RSS only counts resident pages.

Solution

Measure footprint, not RSS: top -l 1 -o mem -stats pid,ppid,mem,cmprs,command (MEM includes compressed memory; CMPRS shows how much of it is compressed), and sysctl -n vm.swapusage. Group by app family. Typical findings and fixes: (1) a next dev (Turbopack) server left running for days grew to a 9 GB footprint (8.9 GB compressed) inside the single next-server process: restart the dev server between work sessions by killing its whole process group (kill -TERM -<pgid>) and relaunching. (2) Docker Desktop reserving 8 GB for its VM while containers use ~2.3 GB (docker stats --no-stream): lower the VM memory limit. (3) The local Supabase analytics (Logflare) and vector containers used ~0.8 GB and ~55% of a core for logs nobody reads: docker stop them when the app does not use the logs API. (4) Other VMs from desktop apps show up as com.apple.Virtualization.VirtualMachine; lsof -p <pid> shows which disk image they opened, which identifies the owner. (5) Long-forgotten terminal sessions each keep their own set of MCP/tool server processes alive. Also check file $(which node): an x86_64 Node under Rosetta on Apple Silicon (common with Intel Homebrew in /usr/local) slows every build and test; Next.js prints a warning about it at startup.

Why

macOS compresses and swaps inactive pages aggressively, so a leaking or over-provisioned process can hold many GB while its RSS looks small. The cost then shows up as system-wide slowness rather than as one obviously large process.

Gotchas

  • ps RSS understates compressed/swapped memory; use top's MEM column or the Memory column of Activity Monitor
  • Freed swap does not drop instantly; pages of other processes stay in swap until touched
  • Stopped containers with restart policy unless-stopped stay stopped after a Docker restart
  • Restarting the dev server mid-run disrupts agents using it; do it between phases

Code Snippets

Find the real memory hogs on a swapping Mac

top -l 1 -o mem -n 30 -stats pid,ppid,mem,cmprs,time,command
sysctl -n vm.swapusage
docker stats --no-stream --format '{{.Name}} {{.MemUsage}} {{.CPUPerc}}'
lsof -p <vm_pid> | grep -Ei '\.(img|raw)'   # which app owns a VM
file "$(command -v node)"                    # x86_64 under Rosetta?

Context

Local development on a 16 GB Apple Silicon Mac with several long-running dev servers, a Docker Supabase stack and agent sessions open for days

Revisions (0)

No revisions yet.