Any Node.js server deploys on Hangar from its repository: Express, Fastify, NestJS, Hono, Koa or plain http. The app runs as a long-lived process, so WebSockets, queues and in-memory state within a process work as they do locally.
Before you start
- A Node.js app in a Git repository with a
startscript, and abuildscript if it needs compiling:
{
"engines": { "node": "22.x" },
"scripts": {
"build": "tsc",
"start": "node dist/server.js"
}
}
1. Listen on the right address
The server must listen on 0.0.0.0 and on a port you know:
const port = Number(process.env.PORT) || 3000;
app.listen(port, "0.0.0.0");
2. Create the service
Create a project, add a service and pick your repository and branch. Leave the builder on Automatic. It installs dependencies with the package manager your lockfile belongs to (npm, pnpm, Yarn or Bun), runs build if it exists and starts the app with start.
3. Set the variables
Add your configuration in the Variables tab. Hangar sets PORT to the domain's container port, so you don't need to:
NODE_ENV=production
4. Deploy and add a domain
Deploy the service. In Settings → Networking, add a domain with container port 3000 and HTTPS on.
Behind the proxy
Hangar terminates TLS and forwards the request. For Express to report the right protocol and client address:
app.set("trust proxy", true);
Fastify takes trustProxy: true in its options.
Shut down cleanly
On every deploy the old container receives SIGTERM before it is stopped. Close the server on that signal so requests in flight finish:
process.on("SIGTERM", () => {
server.close(() => process.exit(0));
});
Adding a database
Add PostgreSQL, MySQL, MongoDB or Redis to the project, in the same region, and set its internal connection URL on the app as DATABASE_URL or REDIS_URL. To run migrations on each release, chain them in the start script:
"start": "prisma migrate deploy && node dist/server.js"
Monorepos
For a workspace with several apps, set each service's root directory, or build with a Dockerfile to control exactly what is installed. See Builds.
Troubleshooting
- 502 on the domain: the app listens on
127.0.0.1or on a different port from the domain's container port. - The build passes but the app exits:
startpoints at a file the build did not produce. Check the output directory. - Wrong Node.js version: add
engines.nodetopackage.json.