Node
A package.json selects the Node runtime and holds the start script that runs your app.
A package.json is required — it both selects the Node runtime and holds the command that starts
your app. On deploy, agenthost installs your dependencies, runs your build script if you have one,
then starts the app with npm start.
- Define a
startscript — it is the entrypoint. Without one,npm starthas nothing to run and the app won't come up. - A
buildscript runs automatically before start, if present (e.g. to compile TypeScript or bundle a frontend). It's skipped when absent. - Dependencies are installed from
package.json(using the lockfile when you include one). Don't shipnode_modules.
"scripts": {
"build": "tsc", // optional — runs if present
"start": "node server.js" // required — how your app starts
}Supported versions are 20, 22 and 24, defaulting to 22 — see Choosing a version.
Listen on the right port
Traffic is routed to port 3000. Read PORT and fall back to 3000, which is what most frameworks
do already:
const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0");Bind to 0.0.0.0, not 127.0.0.1 — a server listening only on localhost is unreachable from
outside its container, and presents as a container that starts and then times out.
What gets built
FROM node:<version>-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci || npm install
COPY . .
RUN npm run build --if-present
FROM node:<version>-slim
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app ./
EXPOSE 3000
CMD ["sh", "-c", "npm start"]Two consequences worth knowing:
NODE_ENV=productionis set at run time, so anything gated on it behaves accordingly. Your build step runs before that, in the build stage, so dev dependencies are available while building.npm cifalls back tonpm installwhen there's no usable lockfile. Shippackage.jsonandpackage-lock.jsontogether or ship neither — a lockfile out of sync with the manifest failsnpm ciand then installs different versions on the fallback.
Common failures
| Symptom | Cause |
|---|---|
| "Missing script: start" | No start script in package.json |
| Starts, then times out | Listening on 127.0.0.1, or on a port other than 3000 |
| Build fails on install | Lockfile out of sync with package.json |
| Module not found for a package you have | node_modules was shipped instead of the manifest |
| TypeScript app starts and exits | start points at a .ts file — point it at the compiled output, and compile in build |
Beyond this
Need a different process manager, a worker, or system packages? Include your own Dockerfile at the
project root and it's used instead of this one — see the escape hatch.