Cloudflare's Workers Builds image has included Nub since July 30, 2026. A package script can call nub or nubx without an install step:

package.json
{
  "scripts": {
    "build": "nub run i18n:compile && nubx vite build"
  }
}

The binary is on PATH in Workers Builds. The change applies to the Workers image, not the separate Pages build image (tracking issue).

Workers Builds

Cloudflare runs an optional build command and then a Wrangler deploy command. Point the build command at the package script that uses Nub; the preinstalled binary means no devDependency is required.

The image currently defaults to Node 24.18.0 and also preinstalls Node 22.23.2. Use .node-version, .nvmrc, or .tool-versions to select a version that both Cloudflare and Nub read.

Read the full docs on developers.cloudflare.com.

Letting Nub run the install

Workers Builds installs dependencies itself before the build command runs, picking the package manager from the lockfile it finds. It does not yet recognize nub.lock, so a Nub-owned project ends up with a second lockfile from whichever manager Cloudflare falls back to. The next nub install then refuses, unable to tell which one owns the project.

Add a build variable so Cloudflare skips its install and Nub owns it:

SKIP_DEPENDENCY_INSTALL=true

Then install from the build command:

nub install && nub run build

Projects that keep an existing pnpm-lock.yaml, package-lock.json, or bun.lock are unaffected — Cloudflare installs with that manager, one lockfile stays on disk, and Nub reads it.

HTTP endpoint

Workers use Cloudflare's fetch handler rather than a Node HTTP server. A minimal aliveness endpoint looks like this:

src/index.ts
export default {
  fetch() {
    return Response.json({
      alive: true,
      provider: "cloudflare",
      runtime: "cloudflare-workers",
    });
  },
};

Point Wrangler at the handler:

wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "nub-app",
  "main": "src/index.ts",
  "compatibility_date": "2026-07-30"
}

If the project defines a build package script, set Settings → Build → Build command to nub run build. Leave it empty for the standalone handler shown here. Workers Builds does not read custom-build settings from wrangler.jsonc; its default deploy command is npx wrangler deploy.

Wrangler bundles the TypeScript handler without a separate local build. Deploy it from a workstation with:

nubx -y wrangler deploy
curl --fail https://nub-app.ACCOUNT.workers.dev
# {"alive":true,"provider":"cloudflare","runtime":"cloudflare-workers"}

Pages

Cloudflare Pages does not include Nub. Add the npm package as a devDependency so the install links both commands into node_modules/.bin:

package.json
{
  "devDependencies": {
    "@nubjs/nub": "^0.7.5"
  },
  "scripts": {
    "build": "nubx vite build"
  }
}

See Managed builders for the same pattern on other hosted build systems.

Read the full docs on developers.cloudflare.com.

Version pinning

Workers Builds provisions its tools with asdf, so a project-root .tool-versions pins Nub and Node together:

.tool-versions
nodejs 24.18.0
nub 0.7.5

A pinned version the image doesn't already carry is downloaded during the build, so the pin holds even when it differs from the image default. Nub reads the same file when resolving its own Node, which keeps the two in agreement from one place.

Adding @nubjs/nub as a devDependency also works, and is the option to reach for when the version belongs in the lockfile alongside the project's other dependencies.

Nub only participates in the build. Deployed Workers execute on Cloudflare's runtime rather than Node with Nub augmentation.