Deploy a Node.js app on Renode
How a RE:NODE Node.js server starts your repository: the start script, the port from SERVER_PORT, Next.js, Vite and TypeScript builds, choosing the Node version, and the errors first deploys hit.
All docs pages
A Node.js server runs your repository from /home/container. The reliable way to start anything on it - Express, Fastify, Next.js, NestJS, a bot - is a start script in package.json that listens on the port in SERVER_PORT.
How it starts#
With a repository linked on the GitHub tab, every start goes like this:
- The branch is fetched into
/home/container(on every start if "Pull the latest commit every time the server starts" or "Restart the server when someone pushes" is on; otherwise on the first start only). PORTis set to the server's port (unless your project setsPORTitself), andHOSTandHOSTNAMEto0.0.0.0.- If
package.jsonhas astartscript:npm install, thennpm run start. - Without a
startscript, the first file that exists amongindex.js,server.js,app.js,main.js,bot.js,index.mjs,index.cjs, thensrc/index.js,src/server.js,src/app.js,src/main.js, then the.tsversions of those names, is started withnode- orts-nodefor a.tsfile.
Relying on step 4 is fragile. Define start.
npm install installs devDependencies too, so build tools such as typescript can stay there when the start script builds.
A minimal app#
Two files are a complete, deployable app:
{ "name": "hello-renode", "private": true, "engines": { "node": ">=20" }, "scripts": { "start": "node index.js" }}const http = require("node:http");// The server's port. PORT is set from SERVER_PORT when the panel starts the repo.const port = Number(process.env.PORT || process.env.SERVER_PORT);if (!port) { console.error("No port: SERVER_PORT is not set"); process.exit(1);}http .createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello from Renode\n"); }) .listen(port, "0.0.0.0", () => { console.log(`Listening on 0.0.0.0:${port}`); });With Express the only lines that matter are the same two: read the port from the environment and bind 0.0.0.0.
const express = require("express");const app = express();app.get("/", (req, res) => res.send("Hello from Renode"));app.listen(Number(process.env.PORT || process.env.SERVER_PORT), "0.0.0.0");Frameworks that need a build#
There is no separate build server: the build runs inside your server, with its memory and CPU. Put the build in the start script, or build locally and commit the output.
| Framework | A start script that works |
|---|---|
| Next.js | next build && next start -H 0.0.0.0 -p $SERVER_PORT |
| Nuxt | nuxt build && node .output/server/index.mjs |
| Vite (preview server) | vite build && vite preview --host 0.0.0.0 --port $SERVER_PORT |
| Angular | ng build && npx http-server dist -a 0.0.0.0 -p $SERVER_PORT |
| TypeScript | tsc && node dist/index.js |
Never use a development server as start - next dev, vite, nodemon. They are slow, unminified, watch the filesystem, use more memory, and often bind localhost, where nothing outside the container can reach them.
TypeScript#
Compile in the start script, or commit the compiled dist/. Do not depend on a global ts-node.
{ "scripts": { "build": "tsc -p tsconfig.json", "start": "tsc -p tsconfig.json && node dist/index.js" }, "devDependencies": { "typescript": "^5.6.0" }}Choosing the Node version#
The Node.js version is the server's runtime image. Pick it under Runtime version on the GitHub tab, or the Docker image on the Startup tab. Declare the version your project expects in engines.node in package.json or in an .nvmrc file - the pre-flight check compares them with the server's runtime and warns when they differ, because packages built for one Node major can fail to install or run on another.
Environment variables#
Upload .env through the Files tab into /home/container, or set variables on the Startup tab. If you use dotenv, load it first:
require("dotenv").config();Commit a .env.example with every key and no values, and keep .env in .gitignore.
Checklist#
package.jsonhas astartscript that starts the production server.- The app listens on
process.env.PORTorprocess.env.SERVER_PORT, on0.0.0.0. - Anything that must be built is built by the start script, or the output is committed.
package-lock.jsonis committed;node_modulesis not.- No password, token or key is committed;
.envis in.gitignore. - The Node version in
engines.nodeor.nvmrcmatches the server's runtime. - Files the app writes at run time go under
/home/container.
Common errors#
The container starts and immediately exits. There is no start script and none of the entry files exist. Add "start": "node <your main file>".
`npm run start` cannot find `dist/index.js` or `.next`. The build never ran. Build in the start script, or commit the output.
The console says it is listening, but the address does not answer. The app is bound to localhost or to a hardcoded port. Use 0.0.0.0 and the port from the environment.
The console stops mid-build with no error. The build hit the memory limit. See the warning above.
Packages fail to install or crash with a native-module error. The Node version differs from what the project was built with. Match the runtime version to engines.node, and never commit node_modules built on Windows or macOS.
More in Troubleshooting.
Questions this does not answer? Open a ticket from the panel, or start from the overview. These pages are also available as plain text for AI assistants at /llms.txt.