# Deploy Node.js, Next.js and Nuxt

> Deploy Node.js, Next.js and Nuxt apps with Falak — build once on the builder, then run a supervised start script on an allocated port behind Caddy.

Source: https://falak.sh/docs/deploy/node/

This guide deploys a server-rendered JavaScript or TypeScript app — Express, Fastify, Hono, NestJS, Next.js, Nuxt, Remix, SvelteKit, Astro SSR — to Falak. The app runs as a supervised process on each server, and Caddy terminates TLS and proxies to it.

## Prerequisites

- A server of type **app**, **web** or **worker** with Node.js (the default server stack installs Node 22).
- A `package.json` with:
  - a **`start` script** — Falak runs `npm run start` in the release;
  - a **`build` script** if your app needs one (Next.js, Nuxt, TypeScript).
- Your app listens on **`process.env.PORT`** (and ideally `process.env.HOST`).

```js title="server.js"
import http from 'node:http';

const port = Number(process.env.PORT ?? 3000);
const host = process.env.HOST ?? '127.0.0.1';

http.createServer((req, res) => res.end('Hello from Falak')).listen(port, host);
```

```json title="package.json"
{
  "scripts": {
    "build": "tsc -p .",
    "start": "node dist/server.js"
  }
}
```

## Deploy

1. **+ Create → Git repository**, choose the repository and branch.
2. Falak detects the stack and suggests **Next.js**, **Nuxt** or **Generic Node**. The runtime is **Node.js**.
3. Pick servers and a domain, then click **Deploy**.
4. The build installs dependencies, runs `build`, prunes dev dependencies and uploads the release. Each server starts the web process and Caddy proxies the domain to it.

## How it runs

| Aspect | Value |
|---|---|
| Web process | `<slug>.app`, supervised by the agent: `npm run start` in `/srv/falak/sites/<slug>/current` |
| Environment | Your site variables + `PORT=<app port>`, `HOST=127.0.0.1`, `NODE_ENV=production` (unless you set it), `PATH=/usr/local/bin:/usr/bin:/bin`, plus `FALAK_SITE`, `FALAK_SITE_ID`, `FALAK_SERVER_ID`, `FALAK_RELEASE_ID`, `FALAK_DEPLOYMENT_ID` |
| App port | Allocated per server from **3000–3999**, shown as `app_port` on the site |
| User | The site user (`falak` by default) |
| Health check | `GET /` expecting `200` (change under **Settings → Deploy**) |
| Deploy script | `$FALAK_FETCH` → `$FALAK_ACTIVATE` → `$FALAK_RESTART_PROCS` |
| On deploy | The web process restarts in the new release |

The web process always runs `npm run start`, whatever package manager built the app. `npm run` works with pnpm and yarn projects because it only runs the script.

## Framework notes

  
    - Preset variables: `NODE_ENV=production`, `NEXT_TELEMETRY_DISABLED=1`.
    - Make sure `start` is `next start` (it reads `PORT` automatically).
    - Build-time public variables must start with `NEXT_PUBLIC_` to reach the build. See [Builds](/docs/concepts/builds/#build-time-variables).
    - APM: add `instrumentation.ts` with `@falak/apm-node/next`. See [APM for Node](/docs/observability/apm-node/).
  
  
    - Preset variables: `NODE_ENV=production`, `NITRO_PRESET=node-server`.
    - `start` should run the Nitro server: `node .output/server/index.mjs`.
    - Public runtime config variables start with `NUXT_PUBLIC_`.
  
  
    - Use the **Generic Node** preset.
    - Listen on `PORT`. Bind to `HOST` (127.0.0.1) so the app is only reachable through Caddy.
  
  
    - Use the Node adapter (`@astrojs/node`, `@sveltejs/adapter-node`, Remix's Node server) and a `start` script that runs the built server.
    - Without an SSR adapter, Astro and Vite apps are [static sites](/docs/deploy/static-sites/).
  

## Workers and cron

Add background processes (`node worker.js`, BullMQ consumers) as **daemons** and scheduled scripts as **cron jobs** in the **Processes** tab. See [Processes](/docs/guides/processes/).

## Node.js versions

Servers offer Node 20 (20.19.5), 22 (22.20.0, default) and 24 (24.9.0). The builder reads your project's Node version. Pick the site's Node version in **Settings → Build**. Sites can select 18, 20, 22 or 24; make sure the server has that version installed.

## Troubleshooting

| Symptom | Cause and fix |
|---|---|
| Health check fails, logs say `Missing script: "start"` | Add a `start` script to `package.json`. |
| 502 from the domain | The app does not listen on `PORT`, or crashed. Check the site's **Logs** tab. |
| `EADDRINUSE` | You hard-coded a port. Use `process.env.PORT`. |
| Build fails with a lockfile error | Commit your lockfile, or override the install with `FALAK_INSTALL_COMMAND`. |
| Environment variable undefined in the browser bundle | Build-time variables need a public prefix (`NEXT_PUBLIC_`, `VITE_`, …) or must be exposed to the deploy script. |

## Next steps
