Fix "esbuild-linux-64" could not be found: Vite in Docker
Why vite dev fails in a node:16-slim container with a mounted frontend folder, why npm rebuild esbuild works there but not on current Vite, and the compose change that holds.
TL;DR#
vite fails inside the container with The package "esbuild-linux-64" could not be found, and is needed by esbuild when the project folder, node_modules included, is bind-mounted from a Mac or Windows host. esbuild ships its executable as a separate npm package per platform, and npm installed only the macOS one on the host. Keep the bind mount for hot reload, but hide the host's node_modules behind an anonymous volume and run the install inside the container. npm rebuild esbuild, the accepted answer, works on the Vite 2 of the question but not on current Vite. On Vite 5.0.0 and 7.3.7 Rollup's native package failed first, and Vite 8 no longer installs esbuild.
The error#
The Stack Overflow question runs the web service from the node:16-slim image, with ./frontend mounted on /app and yarn dev as the command. The same output was reproduced on 19 October 2026 with Vite 2.6.7 and esbuild 0.13.15 on node:16-slim (Node v16.20.2, Yarn 1.22.19), after installing the project for macOS into the mounted folder:
$ vite
failed to load config from /app/vite.config.js
error when starting dev server:
Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild.
If you are installing esbuild with npm, make sure that you don't specify the
"--no-optional" flag. The "optionalDependencies" package.json feature is used
by esbuild to install the correct binary executable for your current platform.
at generateBinPath (/app/node_modules/esbuild/lib/main.js:1643:15)
at esbuildCommandAndArgs (/app/node_modules/esbuild/lib/main.js:1699:11)
at ensureServiceIsRunning (/app/node_modules/esbuild/lib/main.js:1856:25)
at Object.build (/app/node_modules/esbuild/lib/main.js:1749:26)
at bundleConfigFile (/app/node_modules/vite/dist/node/chunks/dep-713b45e1.js:68592:34)
at loadConfigFromFile (/app/node_modules/vite/dist/node/chunks/dep-713b45e1.js:68569:35)
…Every stack frame the question shows matched, line numbers included, down to the dep-713b45e1.js chunk name. Current esbuild releases word the same failure differently, and name both packages. Calling esbuild 0.28.2 directly, in a node:22-slim container over a folder installed for Apple Silicon, printed:
You installed esbuild for another platform than the one you're currently using.
This won't work because esbuild is written with native code and needs to
install a platform-specific binary executable.
Specifically the "@esbuild/darwin-arm64" package is present but this platform
needs the "@esbuild/linux-x64" package instead. People often get into this
situation by installing esbuild on Windows or macOS and copying "node_modules"
into a Docker image that runs Linux, or by copying "node_modules" between
Windows and WSL environments.Both messages come from the same function, generateBinPath, in esbuild's platform lookup code. The second one appears when esbuild finds a package for some other platform on disk.
Why the mounted node_modules breaks esbuild#
As the error message itself says, esbuild is written in native code and needs a platform-specific executable. The esbuild package lists one binary package per operating system and CPU under optionalDependencies, and each of those declares where it runs with the os and cpu fields that the npm package.json documentation describes. npm leaves out the ones that do not match the machine running the install: the simulated Mac install below got a macOS binary and nothing else. With esbuild 0.13 on an Intel Mac, that package is esbuild-darwin-64. On current releases the packages are scoped, such as @esbuild/darwin-arm64.
The question's compose file has no build step. - ./frontend:/app makes the container see the host folder exactly as it is, and that folder's node_modules was installed by macOS. The Linux container asks for the Linux package, finds only the macOS one, and stops. The asker reached the same conclusion in the accepted answer: the container runs Linux, so the architecture is mismatched when it uses the esbuild from the mounted volume.
The failure happens while the config loads, before any of your code runs. Vite 2 bundles vite.config.js with esbuild first, which is why the first line is failed to load config. The config itself is fine. Whatever vite.config.js contains, esbuild can't start.
The reproduction stood in for the Mac with a named Docker volume mounted on /app. A node:22-slim container (npm 10.9.9) installed the project into it with npm install --os=darwin --cpu=x64 --ignore-scripts, so npm picked packages as a Mac would. --ignore-scripts matters: without it, esbuild's own install script would run on Linux and download the Linux binary, and the error would never appear. A directory listing confirmed the volume looked like a Mac install, with esbuild and esbuild-darwin-64 and nothing for Linux.
Fix: keep node_modules out of the bind mount#
The durable fix keeps live reload and stops the host's node_modules from reaching the container at all.
-
Add an anonymous volume on
/app/node_modulesunder thewebservice. Docker mounts it on top of the bind mount, so the container sees that empty volume instead of the host'snode_modules. Docker's volumes documentation covers anonymous volumes and how they are removed. -
Install the dependencies inside the container before starting Vite. The question uses a plain
nodeimage with no Dockerfile, so nothing has installed into that new volume yet. The install has to run incommand:
services:
web:
image: node:22-slim
volumes:
- ./frontend:/app
- /app/node_modules
working_dir: /app
command: sh -c "npm install && npm run dev"
ports:
- "3000:3000"- Recreate the container so that the new volume is created:
docker compose up webStep 2 is not optional. With only the volume line added, the reproduction failed differently. The empty volume hid the Mac packages, and nothing put Linux ones in their place:
> dev
> vite
sh: 1: vite: not foundChanging only the command to npm install && … turned that into a running server. The tested compose file, logged with the run, used a named volume in place of ./frontend, mounted the shared npm cache, wrapped npm run dev in timeout 25, and had no port mapping. With Yarn, as in the question, put Yarn's install command in the same position (only the npm version was run here).
The lockfile is not the problem. The package-lock.json written by the simulated Mac install still had an entry for node_modules/@esbuild/linux-x64, even though that package was never installed. npm keeps the other platforms' optional packages in the lockfile and decides at install time which ones to fetch, so the install inside the container picked the Linux packages from the same project files.
Verify the fix#
Start the service detached, then look at what the container sees:
docker compose up -d web
docker compose exec web ls node_modules/@esbuild node_modules/@rollup
docker compose logs webOn Vite 7.3.7, the container saw the Linux packages and the dev server was up:
node_modules/@esbuild:
linux-x64
node_modules/@rollup:
rollup-linux-x64-gnu
rollup-linux-x64-musl VITE v7.3.7 ready in 235 ms
➜ Local: http://localhost:3000/
➜ Network: http://172.20.0.2:3000/Reading the "host" folder outside the container still showed only darwin-arm64 under node_modules/@esbuild. The Mac keeps its own install, and each side runs its own binaries.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
npm rebuild esbuild: what it does, and when it stops working#
The accepted answer changed the command to sh -c "npm rebuild esbuild && yarn dev". On the question's versions that works, and the reproduction confirmed it:
vite v2.6.7 dev server running at:
> Local: http://localhost:3000/
> Network: http://172.17.0.2:3000/
ready in 384ms.It works because rebuilding re-runs esbuild's install.js inside Linux. That script can't find the Linux package, so it fetches it from the registry, first through a nested npm install, then by downloading the tarball directly (install script source). It saves the binary next to main.js. After the rebuild, node_modules/esbuild/lib contained a new file, downloaded-esbuild-linux-64-esbuild, and esbuild's lookup checks that location when the package is missing. Two consequences follow:
- The container needs the registry. With the downloaded file removed and the container started with
--network none, the rebuild failed:
[esbuild] Trying to download "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz"
[esbuild] Failed to download "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz": getaddrinfo EAI_AGAIN registry.npmjs.org
/app/node_modules/esbuild/install.js:232
throw new Error(`Failed to install package "${pkg}"`);
^
Error: Failed to install package "esbuild-linux-64"- The Linux binary lands in the host's folder. The bind mount is two-way, so the rebuild writes into the Mac's
node_modules. It runs again on every container start, and the Mac now has a Linux binary it never uses.
On Vite 5 to 7, the Rollup error comes first#
Vite 5.0.0 moved to Rollup 4, and Vite 7.3.7 still depends on it (registry metadata). Rollup 4 ships its native code as per-platform packages too, @rollup/rollup-darwin-arm64 and its siblings. With the same macOS-installed folder, Vite 7.3.7 never reached esbuild, and Vite 5.0.0 stopped on the same line:
Error: Cannot find module @rollup/rollup-linux-x64-gnu. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory.On Vite 7.3.7, npm rebuild esbuild was run as a negative control. It downloaded @esbuild/linux-x64 into node_modules/esbuild/lib, and the next npm run dev stopped on exactly the same Rollup line. On a current project, the accepted answer fixes one binary out of several. The volume-plus-install fix covers all of them at once. The Windows side of the Rollup error has its own page: Cannot find module @rollup/rollup-win32-x64-msvc, with the folder installed on Linux and run on Windows.
Vite 8: no esbuild, a Rolldown binding instead#
Vite 8.0.0 (registry metadata for 8.0.0) drops both Rollup and esbuild from its dependencies, lists esbuild as an optional peer, and depends on Rolldown instead. A new Vite 8.3.4 project installed for macOS had no @esbuild folder at all, only @rolldown/binding-darwin-arm64, so the esbuild message in the title cannot appear unless something else installs esbuild. In the Linux container, the dev server stopped with:
Error: Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory.The same compose file, with the anonymous volume and npm install in the command, started it:
VITE v8.3.4 ready in 343 msThe optionalDependencies workaround from the third answer#
One answer suggests adding "@esbuild/linux-x64": "^0.20.1" to optionalDependencies in the host's package.json, so the Mac installs the Linux binary too. Under the same simulation (npm 10.9.9, --os=darwin --cpu=arm64), with the entry set to the installed esbuild version, npm skipped it. Only @esbuild/darwin-arm64 ended up in node_modules/@esbuild. npm still checks the package's own os and cpu fields, even when you list it yourself. esbuild's error message points Yarn users to the supportedArchitectures setting instead, for installing several platforms at once.
When the error changes after the fix#
Installing inside the container exposes you to that image's own install problems. On images that ship Node.js 20.3.0, which upgraded libuv, esbuild's install script can stop with spawnSync … ETXTBSY. That is a libuv io_uring issue, covered in the esbuild ETXTBSY fix for Docker (move to a newer image or set UV_USE_IO_URING=0). The compose file above reinstalls on every start, so a new dependency arrives on the next up. If you later move the install into a Dockerfile and keep the /app/node_modules volume, a newly added package can go missing because the old volume is reused; the Cannot find module 'dotenv' in Docker Compose fix covers that case and how to recreate the volume with -V. When you move from a mounted dev setup to a Dockerfile, .dockerignore decides what reaches the build: excluding node_modules prevents the same mismatch at COPY. Vite's index.html entry error behind a .dockerignore rule shows what goes wrong when the ignore file excludes too much.
The same pattern breaks every tool that ships native code as optional packages. Next.js's Failed to load SWC binary is the @next/swc-* version of it, and Exec format error loading a library is the compiled .node addon version. In each case, the fix is not a flag on the tool. It is an install that runs on the platform that executes the code. For a mounted development container, that means one volume line and one install command.
Further reading: Fix slow Docker ARM64 multi-platform builds on GitLab CI.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix esbuild spawnSync ETXTBSY in Docker: Node 20.3 io_uring
Why npm install of esbuild started failing with ETXTBSY on node:20.3 images in June 2023, what changed in libuv and Docker since, and the build fix that holds today.
Fix Vite 'Top-level await is not available' build error
Why vite dev works but vite build fails on a top-level await, which setting fixes the build and which one only fixes dev, reproduced on Vite 2.9, 5.4, 7.3 and 8.3.
Fix: No loader is configured for ".node" files (Vite)
Trace the import that brings a native addon into the dev scan, and distinguish the tested Vite 4 behaviour from newer bundlers.


