Cloudflare
Workers Builds preinstalls Nub. Cloudflare Pages uses the managed-builder devDependency pattern instead.
Cloudflare's Workers Builds image has included Nub since July 30, 2026. A package script can call nub or nubx without an install step:
{
"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=trueThen install from the build command:
nub install && nub run buildProjects 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:
export default {
fetch() {
return Response.json({
alive: true,
provider: "cloudflare",
runtime: "cloudflare-workers",
});
},
};Point Wrangler at the handler:
{
"$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:
{
"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:
nodejs 24.18.0
nub 0.7.5A 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.
Vercel
Vercel ships no preinstalled Nub, but the install command is yours to replace — so one line puts the CLI on PATH for the rest of the build. The devDependency pattern works too.
Railway
Deploy a Nub web service from a Dockerfile. Railway builds the official Nub image, supplies PORT, and creates a public domain.