Skip to content
Latchkey

How to Run esbuild in CI Across Platforms

esbuild is a native Go binary delivered through per-platform optional dependencies. CI breaks when the installed binary does not match the runner.

esbuild installs a platform-specific package (e.g. @esbuild/linux-x64) as an optional dependency. A lockfile built on another OS, or --no-optional, leaves the wrong binary or none at all.

Why it fails in CI

  • The lockfile was generated on macOS/Windows, so the Linux binary is absent → host mismatch error.
  • npm ci --omit=optional dropped the platform package.
  • A node_modules cached from another OS carries the wrong esbuild binary.

Install and run it reliably

Install fresh on the runner with optional dependencies enabled so the correct platform package resolves. Do not copy node_modules across operating systems.

Terminal
# clean install on the target platform, optional deps ON
npm ci

# verify the platform binary resolved
node -e "console.log(require('esbuild').version)"

Cache & speed

esbuild is tiny and fast; standard ~/.npm caching is plenty. Avoid caching node_modules across OS/arch since the binary is platform-specific.

.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

Common errors

  • Host version "x" does not match binary version "y" → reinstall on the runner; mismatched packages.
  • Cannot find module @esbuild/linux-x64 → optional deps were omitted; reinstall with them enabled.
  • spawn .../esbuild EACCES → the binary lost its executable bit (bad cache restore); reinstall.

Key takeaways

  • esbuild ships a per-platform optional dependency - keep optional deps enabled.
  • Install fresh on the runner; never reuse node_modules across OS/arch.
  • Generate the lockfile on Linux if your CI runs on Linux.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →