Skip to main content

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.

VariablePurpose
RIVET_ENDPOINTWhere your worker reaches the control plane. Carries the secret token, server-side only.
RIVET_PUBLIC_ENDPOINTWhat the metadata endpoint hands to browser clients. Carries the publishable token.

Both use URL auth: https://<namespace>:<token>@<host>.

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/rivet endpoint to start actors, so the URL must be public and registered. Set RIVETKIT_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.

Next steps