# Node.js Quickstart

> Deploy a small Node.js HTTP application as a NexHost Web Service.

Source: https://nexthomelabs.com/docs/quickstarts/node
Markdown: https://nexthomelabs.com/docs-md/quickstarts/node
Slug: quickstarts/node
Section: Quickstarts
Last updated: 2026-08-30
Reading time: 4 min read

---

# Node.js Quickstart

This quickstart deploys a small HTTP application as a [Web Service](/docs/services/web-services). NexHost discovers its reachable TCP listener and assigns ingress automatically, so the application must start a persistent server bound to `0.0.0.0`.

Use [Frontend App](/docs/services/frontend-app) instead for a server-rendered Next.js, Nuxt, Remix, or SvelteKit UI. The contract is the same — start a server bound to `0.0.0.0` — but the frontend service exists so your UI and backend can have separate hostnames and build/start semantics while both using the public-runtime model.

## When to use this quickstart

**Use this page when:**

- The deliverable is a public Node backend — Express, Fastify, Koa, NestJS, or a lightweight `http` server — that should answer browser, mobile, and webhook callers over a hostname.
- You want a five-minute sanity check that your application’s startup shape is correct before you connect a larger repository.

**Skip this quickstart when:**

- The deliverable is a server-rendered frontend — use the [Frontend App](/docs/services/frontend-app) flow, where `next start -p $PORT` is the launchable.
- The deliverable is pre-built static files only — use [Static Site](/docs/services/static-sites).

## Before you begin

- Node.js 18+ locally (for local verification).
- A NexHost workspace and permission to create a project.
- A few minutes on the command line — the guide intentionally avoids tooling magic so the dashboard values match what you just typed.

## Create the application

From an empty parent directory:

```bash
mkdir my-node-app && cd my-node-app
npm init -y
npm install express
```

Create `index.js` at the project root:

```js
const express = require("express");
const app = express();
const port = Number(process.env.PORT);

app.get("/", (_request, response) => response.send("Hello from NexHost"));
app.get("/health", (_request, response) => response.status(200).json({ ok: true }));

app.listen(port, "0.0.0.0", () => {
  console.log(`Listening on ${port}`);
});
```

Why this shape:

- `Number(process.env.PORT)` reads NexHost's compatibility default (`3000`). A fixed internal port is also safe because NexHost discovers the listener and assigns the external port.
- Binding to `"0.0.0.0"` attaches to the container’s network interface, not only to loopback. Binding only to `"127.0.0.1"` makes the runtime observe "connection refused" even when your localhost `curl` succeeded.
- `/health` is optional application monitoring; NexHost readiness is based on TCP reachability rather than this route.

Set the start script in `package.json`:

```json
{ "scripts": { "start": "node index.js" } }
```

> [!TIP]
> Verify locally before you deploy:
>
> ```bash
> PORT=3000 npm start &
> curl -i http://127.0.0.1:3000/health
> # expect 200 with {"ok": true} quickly, no authentication required
> ```

## Configure the service

1. In **New Project**, select **Web Service** — not Frontend App, not Static Site.
2. Choose a supported source and connect this project — a repository checkout, ZIP archive, or local folder. Confirm the branch and root directory when offered.
3. Set **Start command** to `npm start`. The dashboard already knows the install and build commands needed for most Node projects; if you customized them locally, mirror those values here.
4. Deploy and open the generated hostname shown by the dashboard after the release becomes ready.

The server must bind to `0.0.0.0`. It may use `process.env.PORT` or a fixed internal port; NexHost discovers it automatically.

> [!WARNING]
> Do not place secrets in client-inlined variables for a frontend that shares this workspace. A service-scoped secret should not be given a browser-exposed prefix just because another service is a static site.

## What success looks like

- The deployment detail page shows the process log line "Listening on <PORT>" and a TCP-readiness confirmation.
- The service’s **generated hostname** at the dashboard loads `Hello from NexHost` at `/` and `{"ok": true}` at `/health` — both without passing a cookie or header.
- Future environment variable changes on this service apply at the next deployment or re-launched process, depending on whether they were scoped to Build or Runtime.

## Troubleshooting

| Symptom | What to check |
| --- | --- |
| Startup readiness times out | Is `npm start` the start command rather than `npm run build` (which exits immediately)? Does the server bind to `0.0.0.0` and reach its listen call? |
| Connection refused | Binding only to `127.0.0.1` is the classic variant. |
| Build succeeds but process exits quickly | The start command built rather than ran — move the build step to Build and keep a continuous start mechanism in Start. |

See [Deployment Troubleshooting](/docs/deployments/troubleshooting) for the full symptom table and [Logs](/docs/operations/logs) for reading each stage’s output.

## Related documentation

- [Web Services](/docs/services/web-services) — full public-runtime contract for Node backends.
- [Frontend Apps](/docs/services/frontend-app) — the analogous flow for server-rendered frontends.
- [Environment Variables](/docs/configuration/environment-variables) — when to scope a key to Build vs Runtime vs Both.
- [Domains and Networking](/docs/configuration/networking) — public hostnames, custom domains, and the `PORT` contract.

