GuidesLanguage guides
Node.js
Ferry builds a Node.js app from package.json, with npm, Yarn, pnpm or Bun.
Detect. Ferry finds package.json, bun.lock or bun.lockb.
1 / 5
A minimal example
package.json
package-lock.json
server.js
{
"name": "hello-node",
"private": true,
"scripts": { "start": "node server.js" }
}const http = require("http");
const port = Number(process.env.PORT || 3000);
http
.createServer((req, res) => res.end("Hello from Node on Ferry\n"))
.listen(port, "0.0.0.0", () => console.log(`listening on :${port}`));ferry up hello-node --followWhat Ferry generates
| Step | What Ferry generates |
|---|---|
| Base image | node:<major>-alpine. The default is 22. With engines.node in package.json, Ferry uses a major version that satisfies it: "20.x" gives node:20-alpine. Bun projects use oven/bun:1-alpine. |
| Package manager | From the packageManager field. If there is none, from the lockfile: bun.lock/bun.lockb → Bun, pnpm-lock.yaml → pnpm, yarn.lock → Yarn. If there is none, npm. Yarn and pnpm run through Corepack. |
| Build | Your build command. If there is none: <package manager> run build, when package.json has a build script. Ferry sets NODE_ENV=production after the install. Thus the devDependencies are there for the build. |
| Start | With a start script: npm start (yarn start with Yarn 2+, bun run start with Bun). With no start script: node with the main file of package.json, server.js, index.js or app.js. |
The install command
| Package manager | Command |
|---|---|
| npm, with a lockfile | npm ci --include=dev |
| npm, with no lockfile | npm install --include=dev |
| Yarn 1 | yarn install --frozen-lockfile --production=false |
| Yarn 2+ | yarn install --immutable |
| pnpm | pnpm install --frozen-lockfile |
| Bun | bun install |