Repository navigation
Description
Bug report
Docker Desktop's rosetta-wrapper starts every node under Rosetta with --no-opt -r /proc/.reset, the workaround from docker/for-mac#6998. --no-opt turns off V8's TurboFan, but Node 24 and later also optimize with Maglev, V8's mid-tier compiler, which emits the same x87 code for a floating-point remainder. Under Rosetta on macOS 15.2 that code runs thousands of times slower: a function doing x % 0.25 runs fine until Maglev compiles it, then a few thousand more calls take minutes.
We hit it in a real build. Tailwind CSS 4 runs Number(value) % 0.25 for every class-name candidate, and a Vite production build inside an amd64 image sat on "transforming..." for over 40 minutes. The same build takes 1.5 s natively, and 6 s under Rosetta with node --no-maglev, with byte-identical output.
Expected: Node 24+ under Rosetta runs at about the speed Node 20 and 22 do.
Actual: any optimized float % freezes the process at 100% CPU.
Steps to reproduce
// repro.js
function O(e, t) { const r = Number(e); return r >= 0 && r % t === 0 && String(r) === String(e) }
let c = 0, t = Date.now()
for (let i = 1; i <= 30000; i++) {
if (O(String(i / 4), 0.25)) c++
if (i % 5000 === 0) { const n = Date.now(); console.log(`${i} calls: +${n - t} ms`); t = n }
}docker run --rm --platform linux/amd64 -v "$PWD/repro.js:/repro.js:ro" node:26-slim node /repro.js
docker run --rm --platform linux/amd64 -v "$PWD/repro.js:/repro.js:ro" node:26-slim node --no-maglev /repro.js| Node | Result |
|---|---|
| 20.20.2 | 30,000 calls in about 120 ms |
| 22.23.4 | 30,000 calls in about 140 ms |
| 24.22.2 | 5,000 calls in 76 ms, then the next 5,000 not done after 60 s |
| 26.10.0 | 10,000 calls in 121 ms, then the next 5,000 not done after 3 minutes |
26.10.0 with --no-maglev |
200,000 calls in 0.14 s |
The container process shows the wrapper's flags: /run/rosetta/rosetta /usr/local/bin/node node --no-opt -r /proc/.reset .... Docker Desktop 4.37.2 (engine 27.4.0) on the same Mac behaves the same. On an M5 with macOS 27.0.1 (engine 29.7.2) the same build is fast, so a newer Rosetta likely doesn't have the problem; anyone on macOS 15 or older with Node 24+ does.
Why: V8's x64 Float64Modulus loops on x87 fprem / fnstsw / sahf, in TurboFan (src/compiler/backend/x64/code-generator-x64.cc) and in Maglev, which copies it (src/maglev/x64/maglev-ir-x64.cc). Unoptimized code calls C's fmod instead, which is why the first few thousand calls are fast.
Suggestion: add --no-maglev next to --no-opt in the wrapper's Node workaround. Node refuses --no-maglev in NODE_OPTIONS, so users can't easily add it themselves.
Workaround meanwhile: start the affected node with --no-maglev directly. We start Vite through a two-line script in the image build.
Platform
macOS
Version information
- Docker Desktop 4.98.0 (Client and Server 29.9.2), "Use Rosetta for x86_64/amd64 emulation" on (default)
- Also reproduced on Docker Desktop 4.37.2 (engine 27.4.0)
- macOS 15.2 (24C101), Apple M1 Pro
- Images:
node:20-slim(20.20.2),node:22-slim(22.23.4),node:24-slim(24.22.2),node:26.10.0-slim,--platform linux/amd64
Diagnostics ID
No response
Activity
bsousaa commentedon Nov 9, 2026
please include a diagnostics id in your issue report