Running each component
The signaling server and the web client are two long-lived processes, so each needs its own terminal (or a supervisor, as below). Run each block from the repository root. The signaling server needs Node.js 18 or later. It calls the built-infetch, which arrived
in 18, and its own dependencies declare the same floor. package.json has no engines field, so
nothing stops you starting it on something older; it fails at runtime instead. CI runs it on Node
20 and the published image is built on Node 24, so anything in that range is well trodden.
Terminal
Terminal
Terminal
pnpm build then pnpm start) to serve static files. Use pnpm dev only during development, as it enables Next.js hot-reloading and is not suitable for production.
Environment variables
Copy and configure the environment files before starting:Terminal
- In
server/.env:CLIENT_URL=http://localhost:3000 - In
client/.env.local:NEXT_PUBLIC_SOCKET_URL=http://localhost:3001 - In
client/.env.local:NEXT_PUBLIC_SITE_URL, set to your own public address
pnpm build with
NEXT_PUBLIC_SITE_URL unset falls back to https://www.floe.one: your instance would serve
canonical tags, share previews, and a sitemap advertising floe.one instead of itself.
Set it to your own origin, or accept that those tags point somewhere else. It is inlined at build
time, so changing it later means another pnpm build.
NEXT_PUBLIC_SOCKET_URL is inlined when the client is built, so on this path changing it later needs another pnpm build. To change the address without rebuilding, leave it unset and set the unprefixed SOCKET_URL in the environment of the process that runs pnpm start instead; the browser then reads it per request from /api/config. See How the client finds the server. Leave both empty when the client and the signaling server share one origin behind a reverse proxy.
Also set NODE_ENV=production in server/.env. Docker sets it for you; a direct Node run does not. Floe registers its own final error handler that returns a generic JSON error at every setting, so this is not a stack-trace leak. Set it anyway: production is what both the published image and server/.env.example use, so a direct Node run matches what is tested.
Updating an existing deployment
The Docker instructions in Operations assume there is no repository to keep in sync. Running directly with Node means there is, so pull, reinstall, then restart:Terminal
pm2 restart <name>, systemctl restart <unit>, or your own runner). pnpm start serves whatever pnpm build last produced, so skipping the client rebuild leaves the old client running with nothing to indicate it is stale.
Confirm the restart landed by checking that uptime reset:
Terminal
Terminal