Workers
Run your code on your own infrastructure, connected to a control plane.
A worker is any process running your code with the Rivet SDK, whether that is a dedicated container or your existing web app. Workers connect outbound to a control plane, which can be Rivet Cloud or one you run yourself.
Every worker guide is the same five steps with platform-specific commands. This page is the shared part; the platform guides only cover what differs.
Connecting
Two environment variables connect a worker to a control plane.
| Variable | Purpose |
|---|---|
RIVET_ENDPOINT | Where your worker reaches the control plane. Carries the secret token, server-side only. |
RIVET_PUBLIC_ENDPOINT | What the metadata endpoint hands to browser clients. Carries the publishable token. |
Both use URL auth: https://<namespace>:<token>@<host>.
Find both values in the dashboard under Settings → Namespace → Advanced → Backend Configuration.
RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev
RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev
Point at your own control plane, using the admin token you configured.
RIVET_ENDPOINT=http://default:<admin-token>@your-control-plane.example.com:6420
RIVET_PUBLIC_ENDPOINT=https://default@your-control-plane.example.com:6420
Inside a Docker network, the host is the service name. From your laptop against a container, it is localhost.
RIVET_PUBLIC_ENDPOINT is served to browsers. Never put the secret token in it.
Runtime modes
How a worker registers depends on how its platform runs it.
- Runner is the default. Your process starts, opens an outbound connection to the control plane, and holds it. It needs no public URL, no ingress, and nothing pasted into the dashboard. Use this on Kubernetes, ECS, Railway, Render, VMs, and Docker Compose.
- Serverless suits request-driven platforms that have no long-lived process. The control plane calls your
/api/rivetendpoint to start actors, so the URL must be public and registered. SetRIVETKIT_RUNTIME_MODE=serverless. Use this on Vercel, Cloudflare Workers, Supabase Functions, Lambda, Cloud Run, and Freestyle.
The platform guides say which mode applies and register the URL where one is needed.
Runner configs
A runner config declares a pool on the control plane. A worker connecting to a pool that has no config is refused, so this must exist before any worker can run actors. The default pool is named default.
Rivet Cloud creates the config for you when you connect a namespace through the dashboard. On a self-hosted control plane, create it once per namespace:
curl -X PUT "http://<control-plane-host>:6420/runner-configs/default?namespace=default" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"datacenters":{"default":{"normal":{}}}}'
Use normal for runner mode. Serverless pools use serverless instead and carry the URL the control plane calls, which the serverless platform guides cover.
Without it the worker starts cleanly and then retries forever, and actor calls fail with no_runner_config_configured.
Packaging
Runner-mode platforms take a container. A minimal Node image:
FROM node:24-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]
Rivet requires Node 22 or newer, so pin an active LTS. Prefer the Debian-based images over Alpine: Rivet’s SQLite dependency ships no musl prebuilt binary, so Alpine falls back to compiling it from source and needs a full build toolchain in the image.
Runner-mode workers listen on no port, so the image sets none. Serverless platforms inject their own PORT.
Graceful shutdown
Actors drain for up to 30 minutes when a worker stops. Give the process at least 35 minutes between SIGTERM and SIGKILL, and make sure your process actually receives the signal: a wrapper such as npm start becomes PID 1 and swallows it. Invoke your binary directly, or run dumb-init or tini as PID 1.