Backend on Render
HTTP service, WebSocket service, and flush worker as Render services.
All three backend services build with tsc -b and start with node dist/index.js.
Only Web Service, Postgres, Key Value, and static sites support free instances, so
on the free tier every service here is a Render web service — the background-worker
service type is paid-only.
Deploy the HTTP service.
Build tsc -b, start node dist/index.js, listening port from PORT (default
5000). It exposes /health, which is also the keep-alive target. Give it
DATABASE_URL, REDIS_URL, and the auth, origin, ticket, email, and OAuth
variables from the environment page.
Deploy the WebSocket service.
Build tsc -b, start node dist/index.js, listening port from WS_PORT
(default 8080, a separate variable from PORT). It answers only WebSocket
upgrades and exposes no HTTP route. Give it DATABASE_URL, REDIS_URL,
PRESENCE_TICKET_SECRET (must match the HTTP service), WEB_ORIGIN, and
WS_ALLOWED_ORIGINS.
Deploy the flush worker as an always-on service.
Same build and start commands. It runs a continuous BullMQ process against
DATABASE_URL and REDIS_URL, so it needs paid uptime — pinging cannot run it
(see the keep-alive page). Without it, room state stays in Redis and snapshots
never become durable revisions.
Last verified: 2026-10-11. Free-tier limits change — check the providers before relying on any number.
Provider docs
What it owns
- The scenes/rooms API as a Render web service
- The room transport as a Render web service
- Snapshot flushes as an always-on Render worker
Talks to
Sources: apps/http-server/package.json, apps/http-server/src/index.ts, apps/http-server/src/app.ts, apps/ws-server/package.json, apps/ws-server/src/index.ts, apps/ws-server/src/server.ts, apps/flush-worker/package.json, apps/flush-worker/src/index.ts (documented at commit b0026d9)