Back

Deploy BAML with a Nuxt 4 (Nitro) server BAML on Vercel Functions

December 28, 2025

In this guide, you will learn how deploy a Nuxt 4 (Nitro) server that uses @boundaryml/baml on Vercel Functions.

Because BAML uses native Node bindings (via napi-rs), it can be cumbersome to configure Nitro properly to avoid bundling errors or ERR_MODULE_NOT_FOUND at runtime.

Why builds/runtime fail

  • @boundaryml/baml uses napi-rs and loads platform-specific .node at runtime via dynamic require() in native.js.
  • If bundled, Rollup/Vite tries to parse .node files (fails) or bakes absolute paths like /vercel/path0/... that don’t exist at runtime (/var/task), causing ERR_MODULE_NOT_FOUND.

Solution

To avoid these issues, we want our bundler (Rollup) to treat BAML as external dependency. Then, we need to ensure Nitro includes the native binaries in the output so they can be resolved at runtime.

We want to configure Nitro as follows:

  • Do NOT bundle BAML. Mark it external at the Rollup level.
  • Rewrite any resolved paths back to a bare specifier so Node resolves them at runtime.
  • Force Nitro’s file tracer to include BAML’s native packages in the serverless output.

Use this minimal config:

// nuxt.config.ts

export default defineNuxtConfig({
  // your config
  nitro: {
    // Ensure BAML native binaries are traced into the server bundle
    externals: {
      traceInclude: [
        'node_modules/@boundaryml/baml',
        'node_modules/@boundaryml/baml-linux-x64-musl',
        'node_modules/@boundaryml/baml-linux-x64-gnu',
        // add arm if your Vercel project uses arm64 runtime:
        // 'node_modules/@boundaryml/baml-linux-arm64-musl',
        // 'node_modules/@boundaryml/baml-linux-arm64-gnu',
      ],
    },
    rollupConfig: {
      // Keep BAML external (don’t bundle native bindings)
      external: [/^@boundaryml\/baml/],
      output: {
        // Prevent absolute paths like /vercel/path0/... from being baked into the bundle
        paths: (id: string) => {
          if (id.includes('@boundaryml/baml')) {
            const match = id.match(/@boundaryml\/baml(\/[^/]+)?$/)
            if (match) return `@boundaryml/baml${match[1] || ''}`
          }
          return id
        },
      },
    },
  },
})

I haven't tested it, but it should work similar for other Nitro-based projects.

Key insights

Our implementation ensures:

  • build: BAML stays external (not bundled)
  • output: references are rewritten to bare specifiers (@boundaryml/baml/native)
  • runtime: Node resolves module from node_modules, and Nitro’s trace includes the correct .node files

Additionally, you can:

  • Avoid nitro.externals.inline for BAML (bundling .node can result in ‘out of memory’ errors).
  • Avoid vercel.json functions.includeFiles for Nitro output; rely on externals.traceInclude instead.

Troubleshooting

  • Still seeing ERR_MODULE_NOT_FOUND? Check which platform Vercel runs (x64 vs arm64, gnu vs musl) and add the matching traceInclude paths.
  • Don’t use externals.inline for BAML.
  • Ensure your build runs on Linux (Vercel does) so the correct platform binary is installed by pnpm during build.

This guide was partly generated with the help of an LLM.