DocsNode (Express and Fastify)

Node (Express and Fastify)

Deploy a plain Node server on Ruust: Express or Fastify, built from your package.json and served on HTTPS.

Ruust runs a plain Node server as an Egg. It detects Node from the package.json at your repo root, installs your dependencies and hatches a long-running server. Express and Fastify both work the same way: the one rule that matters is the port contract below. If your repo has a Dockerfile, Ruust builds that instead of using Nixpacks.

Deploy it

  1. Push your app to a Git repo with a package.json at the root.
  2. In the dashboard, choose New Egg and connect the repo.
  3. Pick a size and a region (London or Virginia), then lay the Egg.
  4. Ruust builds with Nixpacks, hatches the server and gives you a <name>.<region>.ruust.run URL on HTTPS.

The port

Ruust sets a PORT environment variable. Your server MUST listen on process.env.PORT and bind to 0.0.0.0, not localhost or 127.0.0.1. Binding to localhost is the top reason an Egg builds but never hatches: the health check cannot reach it, so it never goes live.

javascript
const express = require('express');
const app = express();

app.get('/', (req, res) => res.send('Hatched.'));

const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0', () => {
  console.log(`Listening on ${port}`);
});
Express: listen on PORT, bind to 0.0.0.0.
javascript
const Fastify = require('fastify');
const fastify = Fastify();

fastify.get('/', async () => ({ status: 'hatched' }));

const port = Number(process.env.PORT) || 3000;
fastify.listen({ port, host: '0.0.0.0' })
  .then((address) => console.log(`Listening on ${address}`))
  .catch((err) => { console.error(err); process.exit(1); });
Fastify: pass host 0.0.0.0 in the listen options.

The start command

The Nixpacks build needs to know how to boot your server. Set a start script in package.json, or add a Procfile at the repo root. Nixpacks reads the Procfile and bakes its web: line into the image as the start command, and where both are present its precedence rules decide which wins (this is Nixpacks behaviour, not a Ruust guarantee). None of this applies when you supply your own Dockerfile, where the image CMD/ENTRYPOINT is authoritative and the Procfile is ignored.

json
{
  "scripts": {
    "start": "node server.js"
  }
}
package.json: the start script.
text
web: node server.js
Procfile at the repo root pins the start command.

Environment variables

Set environment variables on the Egg. They are available at both build time and run time, encrypted at rest and never printed in logs. Read them with process.env.MY_VAR. Plain Node has no client bundle, so there is no public prefix to worry about here (that only applies to front-end frameworks).

Always on

An Egg is a long-running server with no cold starts, so background timers, websockets and in-memory state all keep working between requests. To let two Eggs in the same Coop talk to each other, turn on private networking and peer them: connected Eggs reach each other by a private hostname, and everything else is denied by default.