1. Project Overview & Quickstart (rivet-dev/frontend)
File: README.md
Rivet Hub
[!WARNING]
Repository has been moved to rivet-gg/rivet.
This is the source code for the Rivet Hub frontend
Also check out the Rivet backend for issues, documentation, and more.
Requirements
Getting started
Run the following command:
yarn startThis will open http://localhost:5080 in your browser. By default, this will connect to Rivet's staging servers (https://staging2.gameinc.io).
Environment variables configuration
- Copy
apps/hub/.env.exampletoapps/hub/.env.development.localand change variables if needed.
Developing with self-hosted backend
Where do I self-host the backend?
See our backend repo here.
API Endpoint Configuration
To configure the hub to connect to your own server (for example, mydomain.com), update the .env file in apps/hub folder to include the following:
VITE_APP_API_URL=https://api.mydomain.comBackend configuration
If self-hosting your own backend, the default configuration assumes the hub will be hosted at https://hub.{your domain}. You may want to change the following namespace config parameters:
dns.hub_origin (default: https://hub.{main domain})
This configures the origin that the API will build URLs for.
For example, identity.getProfile returns a URL to the hub profile in identity.external.profile.
rivet.api.hub_origin_regex (default: ^https://hub\\.{main domain}$)
This configurs the CORS origin regex. This allows you to secure where requests can come from to your hub.
If you are running your own cluster for development, consider updating the CORS rules to include localhost similar to below.
CORS
staging2.gameinc.io CORS is specially configured to have the following origin regex:
^https?://(hub\\.rivet\\.gg|[^\\.]+\\.rivet-hub\\.pages\\.dev|localhost:\\d+|192\\.168\\.\\d+\\.\\d+:\\d+|0\\.0\\.0\\.0\\.:\\d+)$This lets you develop with the hub using localhost.
Filing issues
All Hub issues are managed in the main Rivet repository.
๐ Documentation
Technologies
- React
- TanStack Router
- TanStack Query
- Tailwind CSS
- Storybook
- TypeScript
- Yarn
- Biome
- shadcn/ui
- React Hook Form
- Zod
- Turbo
File structure
apps/- Contains the main applications.hub/- The Rivet Hub application.src/- Contains the source code.components/- Contains the components used in the application.contexts/- Contains the React contextsforms/- Contains the forms.layouts/- Contains the layouts, used in routing.queries/- Contains the TanStack queries and mutations configs.routes/- Contains the routes.views/- Contains the views.
docs/- The Storybook application.
packages/- Contains the shared componentscomponents/- Contains the shared components.icons/- Contains icons used by Rivet project, read more here.
Routing
The routing is done using the TanStack Router. The routes are defined in the apps/hub/src/routes folder. For more information, check the TanStack Router documentation. The project is using TanStack's Router Vite integration for dynamically generating route types. This means that all parameters and query parameters are typed and validated.
Routes
To define a route, create a new file in the apps/hub/src/routes folder. The route file should export a Route constant. See already existing routes for examples.
Layouts
To define a reusable layout, you can create a layout component in the apps/hub/src/layouts folder and use it in the route definition. See already existing layouts for examples.
Data fetching
The data fetching is done using the TanStack Query. The queries are defined in the apps/hub/src/queries folder. For more information, check the TanStack Query documentation.
Query and mutation configs
All query and mutation configs are grouped by the business logic they are related to. For example, all queries related to the games are in the games.ts file. Configs define the query key, the query function, and other query options, like the query cache time or the query selector. All queries are cached by default in the local storage. To adjust the cache time, consult the TanStack Query documentation.
Forms
The forms are created using the React Hook Form library. The form schemas are defined using the Zod library. The forms are defined in the apps/hub/src/forms folder. For more information, check the React Hook Form documentation and the Zod documentation. For convenience, the form factory is used to create the forms. The form factory is defined in the apps/hub/lib/create-schema-form.tsx file.
Reusable components (across the Rivet products)
The reusable components are defined in the packages/components/src folder. The components are grouped by the business logic they are related to. For example, all components related to the games are in the games folder. The components are using the shadcn/ui library for styling. The atom components (like buttons, inputs, etc.) are defined in the packages/components/src/ui. Atom components represent the smallest components that can be used in the application, like buttons and inputs. Please keep the components as small and reusable as possible.
Hub components
The components used in the Rivet Hub are defined in the apps/hub/src/components folder. The components are grouped by the business logic they are related to. For example, all components related to the games are in the games folder. The components are using the same technology stack as the reusable components. The components here are more specific to the Rivet Hub and are not meant to be reused across the Rivet products.
Views
Views are special components that are used in the routes, following MVC pattern. They are defined in the apps/hub/src/views folder. The views are using the components and the queries to display the data. The views are not meant to be reused across the Rivet products.
Configuration
The project is designed to be built once and used in multiple environments. UI package uses a React context to determine environment configuration. Hub app, on the other hand, uses an HTML element to fetch the configuration and pass it to the UI elements. To configure the project put the following element in the public/index.html file:
<script type="application/json" id="RIVET_CONFIG">
{ "apiUrl": "YOUR API URL", "assetsUrl": "YOUR ASSETS URL" }
</script>and fill it with the appropriate values.
However, if you want to just deploy the project normally, supply the environment variables:
VITE_APP_API_URL=YOUR
VITE_APP_ASSETS_URL=VALUESwhen building the project.
Troubleshooting
`Failed to resolve import "./some_file.js" from "../../packages/components/dist/index.js". Does the file exist?` error
This is caused by the race condition between the Vite server (responsible for building Hub) and the Rollup build (responsible for building UI). To fix this, stop the Vite server remove node_modules from the hub directory, and run the Vite server again.
`[postcss] Cannot find module '/your/path/to/project/node_modules/@rivet-gg/components/dist/index.cjs'` error
See the solution for the previous error.
Unknown issue with icons, most of the icons are squares or not showing
See troubleshooting section for @rivet-gg/icons package.
๐๏ธ Contributing
- Look for any issue that describes something that needs to be done - or, if
you're willing to add a new feature, create a new issue with an appropriate
description. - Submit your pull request.
- Rivet team will review your changes.
- Don't forget to join Rivet's Discord to hang out
with the devs, or to pairprogram together!
File: packages/icons/README.md
Rivet Icons
Motivation
Rivet Icons are a set of SVG icons that are used in Rivet products. This package is built on top of great Font Awesome icons.ย Some icons used in our products are from the premium Font Awesome icon set. We've created a package that lets you use premium icons without having to buy a Font Awesome license. This is achieved by swapping premium icons with a simple rectangle.
Contributing
Prerequisites
Obtain a Font Awesome Pro license and set the FONTAWESOME_PACKAGE_TOKEN environment variable to your Font Awesome Pro token. This is required to download the premium icons.
Adding new icons
- Modify
generateManifest.jsto include new icons. - Run
./scripts/generateManifest.jsto generate a newmanifest.jsonfile.- If you're getting an error:
Could not find package @fortawesome/pro-solid-svg-icons,
- If you're getting an error:
- Commit the changes to the
manifest.jsonfile. - Run
yarn rebuild @rivet-gg/iconsto generate a new icon set with the new icons.
Troubleshooting
Icons not showing, or icons are rectangles
Some icons used in the open-source Rivet products are part of the premium Font Awesome icon set. We can't share those icons publicly, without violating FA's Terms Of Service. By default, after adding this package to any project, a post install script generates an icon set with all premium icons replaced with simple rectangles. So, you can reference premium icons, but you won't see them. To be able to see premium icons, make sure an environment variable FONTAWESOME_PACKAGE_TOKEN is set when running yarn install. You can re-generate our icon set by running yarn rebuild @rivet-gg/icons if your dependencies are already installed.
Can't start/build project
The description of these kinds of errors may vary. This package heavily depends on postinstall scripts from Yarn/npm. Make sure you didn't disable them accidentally. Turn on post-installation script support and run yarn install or yarn rebuild @rivet-gg/cions again. If there is still a problem, please contact us on Discord.
`Could not find package @fortawesome/pro-solid-svg-icons` when generating manifest
Make sure you have installed pro icons. If you're getting an error, you can try to install them manually by running yarn add @fortawesome/pro-solid-svg-icons, or run yarn install in src folder. Finally run ./scripts/generateManifest.js again.
2. Official Technical Reference & Guides (rivet-dev/website)
File: README.md
Rivet Website
The source for rivet.dev โ marketing pages, docs, and blog.
Each product's documentation is authored in that product's own repository and
vendored in here. See CLAUDE.md for the layout andHIDDEN.md for what is deliberately unpublished.
Develop
pnpm install
pnpm assemble # link each product's docs bundle
pnpm devLicense
File: cloud/docs/content/docs/compute.mdx
title: "Rivet Compute"
Rivet Compute runs your backend for you. You supply a container image; Rivet builds it, pushes it to its own registry, provisions a managed pool, and gives you a URL.
Requirements
- A Rivet Cloud account and project
- Docker running locally
- Your app. If you do not have one yet, start from the Quickstart.
Steps
At your project root:
FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "src/server.js"]Your server must listen on the port in RIVET_PORT, which defaults to 3000.
- Open the dashboard and go to your project.
- Click Connect, then Rivet Cloud.
- Copy
RIVET_CLOUD_TOKEN.
This is a cloud_api_* management token, not a pk_* publishable key. See Tokens.
npx @rivetkit/cli deploy --token cloud_api_xxxxxThe token is written to ~/.rivet/credentials, so later deploys can omit the flag. The CLI resolves your project from the token, builds and pushes the image, upserts the managed pool, and prints the deployment URL once the pool is ready.
The dashboard shows live status:
| Status | Meaning |
|---|---|
| Initializing | Starting the runtime environment |
| Deploying | Pulling and launching your container |
| Binding | Connecting the runner to the network |
| Ready | Live, and accepting actor connections |
If you are an agent polling the API rather than watching the dashboard, use the managed-pool endpoint on the Cloud API.
RIVET_CLOUD_TOKEN is a cloud_api_* token scoped to cloud-api.rivet.dev. Send it as Authorization: Bearer .... A pk_* publishable key is scoped to the Engine API at api.rivet.dev and will 401 here.
Substitute $CLOUD_API_URL (usually https://cloud-api.rivet.dev), $PROJECT, $ORG, $CLOUD_NAMESPACE, and $CLOUD_TOKEN. Poll every 5 seconds until status is ready. Stop and investigate if status is error.
curl -s "$CLOUD_API_URL/projects/$PROJECT/namespaces/$CLOUD_NAMESPACE/managed-pools/default?org=$ORG" -H "Authorization: Bearer $CLOUD_TOKEN"Once local deploys work, install the GitHub Actions workflow:
npx @rivetkit/cli setup-ciThis writes .github/workflows/rivet-deploy.yml. Add the token as a repository secret:
gh secret set RIVET_CLOUD_TOKENThe workflow deploys production on push to your default branch and creates an isolated namespace per pull request. See Preview Deployments.
Logs
rivet logs prints the last 100 lines from the default pool in the production namespace, oldest first, then exits.
npx @rivetkit/cli logsToken resolution matches deploy: the --token flag, then RIVET_CLOUD_TOKEN, then ~/.rivet/credentials.
Add --follow (-f) to stream instead of fetching history:
npx @rivetkit/cli logs --followEach line is <timestamp> [<SEVERITY>] <region> <message>:
2026-06-16T18:26:51.160Z [INFO] eu-central-1 server listening on port 3000
2026-06-17T11:24:20.425Z [ERROR] us-east-1 failed to connect to upstreamMore examples:
# Last 200 lines from a specific namespace
npx @rivetkit/cli logs --namespace production -n 200
# A specific compute pool
npx @rivetkit/cli logs --pool my-pool
# Live tail, filtered
npx @rivetkit/cli logs --follow --contains error
# JSON, piped to jq
npx @rivetkit/cli logs -n 50 --json | jq .Troubleshooting
If the status stays in Initializing for more than a few minutes:
- Check that
RIVET_CLOUD_TOKENis set correctly as a repository secret. - Check the GitHub Actions run logs for a failed step.
The container is failing to start or exiting immediately. Test the image locally with docker run first. Common causes:
- The server file never calls
registry.start(). - A runtime crash on startup.
- The server is not listening on
RIVET_PORT(default3000).
Pricing
You are billed for the compute your deployment uses while it is running.
File: cloud/docs/content/docs/index.mdx
title: "Rivet Cloud"
Rivet Cloud runs the control plane for you. You keep writing the same code and deploy it one of two ways:
- Bring your own compute. Run workers on your own infrastructure and point them at Rivet Cloud. Every platform in the Self-Host worker guides works this way.
- Rivet Compute. Hand Rivet a container and it runs your backend as well. See Rivet Compute.
Nothing here is required to use Rivet. If you would rather run the control plane yourself, see Self-Host.
Getting started
- Create a project at dashboard.rivet.dev.
- Open Connect and pick how you want to deploy.
- Copy the endpoint values, or the cloud token if you are deploying with Rivet Compute.
Tokens
Rivet Cloud issues three kinds of token, and mixing them up is the most common source of 401s.
| Token | Prefix | Scope | Used for |
|---|---|---|---|
| Secret | sk_ |
Engine API at api.rivet.dev |
RIVET_ENDPOINT, server-side only |
| Publishable | pk_ |
Engine API at api.rivet.dev |
RIVET_PUBLIC_ENDPOINT, safe for browsers |
| Cloud API | cloud_api_ |
Cloud API at cloud-api.rivet.dev |
rivet deploy, CI, managed-pool APIs |
A pk_ token will fail against the Cloud API, and a cloud_api_ token will fail against the Engine API. They are not interchangeable.
In this section
- Rivet Compute: deploy your backend as a container
- Preview Deployments: an isolated namespace per pull request
File: cloud/docs/content/docs/preview-deployments.mdx
title: "Preview Deployments"
A preview deployment gives each pull request its own Rivet namespace, so branch work never touches production state. The namespace is created when the PR opens and destroyed when it closes.
Which setup you use depends on where your workers run.
With Rivet Compute
setup-ci writes a workflow that already does this:
npx @rivetkit/cli setup-ci
gh secret set RIVET_CLOUD_TOKENThe generated .github/workflows/rivet-deploy.yml deploys production on push to your default branch, creates a namespace per pull request, posts the preview link as a comment, and cleans the namespace up on close. See Rivet Compute.
With Vercel
Use the preview-namespace-action. It creates the namespace and writes RIVET_ENDPOINT and RIVET_PUBLIC_ENDPOINT into the Vercel preview environment, so the deployment picks them up with no manual configuration.
RIVET_CLOUD_TOKENfrom the dashboard under Settings โ Advanced โ Cloud API TokensVERCEL_TOKENfrom Vercel account settings
name: Rivet Preview
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
concurrency:
group: rivet-preview-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
rivet-preview:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: rivet-dev/preview-namespace-action@v1
with:
platform: vercel
rivet-token: ${{ secrets.RIVET_CLOUD_TOKEN }}
vercel-token: ${{ secrets.VERCEL_TOKEN }}The action also configures the Vercel Deployment Protection bypass header, which is otherwise a common source of 401s from Rivet into preview deployments. See Vercel.
On other platforms
Any platform works, as long as the pull-request build gets endpoint values for a fresh namespace instead of production's. Create the namespace with the Cloud API using a cloud_api_* token, inject the resulting endpoints as build-time environment variables, and delete the namespace when the pull request closes.
Next steps
File: packages/icons/README.md
Rivet Icons
โ ๏ธ Legal Notice
This package is licensed exclusively for use in Rivet products and services. Using this package in any other product, project, or commercial application is strictly prohibited and may constitute a violation of Font Awesome's Terms of Service and intellectual property rights.
This package includes icons from Font Awesome Pro, which are licensed to Rivet for use in Rivet products only. If you wish to use Font Awesome Pro icons in your own projects, you must obtain your own license from Font Awesome.
Overview
Rivet Icons is an icon library built on Font Awesome that provides pre-generated SVG icons for use in Rivet products. All icons (including Pro icons) are committed to the repository, making this package work out-of-the-box with no Font Awesome token required for end users.
Installation
pnpm add @rivet-gg/iconsUsage
import { Icon, faCheckCircle } from "@rivet-gg/icons";Contributing
Prerequisites
For maintainers only: Obtain a Font Awesome Pro license and set the FONTAWESOME_PACKAGE_TOKEN environment variable to your Font Awesome Pro token. This is only required when vendoring new icons.
Adding new icons
- Ensure you have a
FONTAWESOME_PACKAGE_TOKENenvironment variable set - Modify scripts/generate-manifest.js to include new icons
- Run
pnpm manifestto generate a newmanifest.jsonfile- If you're getting an error about missing packages, run
pnpm installin thesrcfolder first
- If you're getting an error about missing packages, run
- Run
pnpm vendorto generate icon files:src/index.gen.jssrc/index.gen.tsdist/index.js
- Commit all changes including the generated files
Troubleshooting
Icons not showing up
All icons (including Font Awesome Pro icons) are pre-generated and committed to this repository. If icons aren't showing:
Make sure you have the peer dependencies installed:
@fortawesome/fontawesome-svg-core@fortawesome/free-solid-svg-icons@fortawesome/free-brands-svg-icons@fortawesome/react-fontawesomereactandreact-dom
Check that you're importing icons correctly:
tsximport { Icon, faCheckCircle } from "@rivet-gg/icons";If the problem persists, contact us on Discord
For Maintainers: `Could not find package @fortawesome/pro-solid-svg-icons`
This error occurs when running the vendor script without access to Font Awesome Pro packages. Make sure:
FONTAWESOME_PACKAGE_TOKENenvironment variable is set- Run
pnpm installin thesrcfolder to install FA Pro packages - Run
pnpm vendoragain
File: src/content/self-host/control-plane/aws-ecs.mdx
title: "AWS ECS"
Requirements
- An AWS account with ECS and Fargate available
- An EFS file system, or an RDS PostgreSQL instance, for persistence
- An Application Load Balancer in front of the service
- AWS Secrets Manager, or SSM Parameter Store, for the admin token
Steps
Fargate task storage is ephemeral, so the data directory must live outside the task.
- Single node: create an EFS file system and mount it into the task at
/data. - Multi-node: create an RDS PostgreSQL instance instead and skip the volume. Use the direct endpoint, not RDS Proxy, which is not supported. See
Storage .
aws secretsmanager create-secret \
--name rivet/admin-token \
--secret-string "$(openssl rand -hex 32)"Reference it from the task definition's secrets block rather than putting it in environment.
Use a pinned image tag, expose both ports, and mount the volume.
{
"family": "rivet-engine",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "1024",
"memory": "2048",
"containerDefinitions": [
{
"name": "rivet-engine",
"image": "rivetdev/engine:2.3.3",
"portMappings": [
{ "containerPort": 6420 },
{ "containerPort": 6421 }
],
"environment": [
{ "name": "RIVET__FILE_SYSTEM__PATH", "value": "/data" }
],
"secrets": [
{
"name": "RIVET__AUTH__ADMIN_TOKEN",
"valueFrom": "arn:aws:secretsmanager:REGION:ACCOUNT:secret:rivet/admin-token"
}
],
"mountPoints": [
{ "sourceVolume": "rivet-data", "containerPath": "/data" }
]
}
],
"volumes": [
{
"name": "rivet-data",
"efsVolumeConfiguration": { "fileSystemId": "fs-XXXXXXXX" }
}
]
}1 CPU and 2 GB of RAM is the recommended minimum per instance.
The ALB default is 60 seconds, which severs long-lived WebSockets and causes reconnect storms.
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn <arn> \
--attributes Key=idle_timeout.timeout_seconds,Value=3600Point the target group at container port 6420 and terminate TLS on the listener. See
Set the target group health check path to /health on port 6421, with a five second timeout.
aws ecs describe-services --cluster <cluster> --services rivet-engine \
--query 'services[0].deployments[0].rolloutState'Expect COMPLETED. Then open /ui on the load balancer hostname and log in with the admin token.
A runner config declares a pool that workers connect to. Without one the control plane refuses every worker, so create it before deploying any:
curl -X PUT "http://<load-balancer-host>/runner-configs/default?namespace=default" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"datacenters":{"default":{"normal":{}}}}'Use normal for long-lived workers. See
Scaling past one task
EFS-backed file system storage is single-node: RocksDB cannot be shared between tasks. Before raising the desired count above one, move to RDS PostgreSQL and add NATS for pub/sub. See
Once on RDS, ECS Service Auto Scaling can drive the desired count. Use target tracking on average CPU at 60%, matching the
Next steps
Production checklist Deploy workers on AWS ECS Configuration reference
File: src/content/self-host/control-plane/backups.mdx
title: "Backup & Restore"
Everything the control plane persists lives in its storage backend, so backing up the control plane means backing up that backend. The engine itself is stateless between restarts and needs no separate backup.
What you must be able to restore:
- The storage backend. Actor state, namespaces, and runner configuration.
- Your configuration. The config file or environment variables, including the admin token. Losing the admin token locks you out of the dashboard and the API.
By backend
Use your provider's automated backups, or pg_dump on a schedule. Point-in-time recovery is worth enabling if your provider offers it.
pg_dump "postgresql://user:password@host:5432/rivet" --format=custom --file=rivet-$(date +%F).dumpRestore into an empty database, then start the control plane against it:
pg_restore --dbname="postgresql://user:password@host:5432/rivet" --clean --if-exists rivet-2026-01-01.dumpManaged providers generally do this better than you will. Prefer their automated backups plus a standby replica over a cron job.
Stop the control plane before copying the data directory. RocksDB is not safe to copy while a process holds it open, and a copy taken from a running node can restore into a corrupt state.
# Stop the control plane first
tar czf rivet-data-$(date +%F).tar.gz -C /var/lib/rivet dataRestore by extracting into the same path and starting the control plane again. If you cannot tolerate the downtime this implies, that is a signal to move to PostgreSQL.
FoundationDB has its own backup agent and continuous backup support. Backup topology is covered during enterprise onboarding. Contact enterprise support.
Test the restore
A backup you have never restored is not a backup. Restore into a scratch environment on a schedule and confirm:
- The control plane starts and reports healthy on
/health. - The dashboard loads and your namespaces are present.
- A worker connects and appears under Runners.
- An actor created before the backup still resolves and reads its state.
Next steps
Storage backends Upgrades Production checklist
File: src/content/self-host/control-plane/configuration.mdx
title: "Configuration"
import EngineConfigSchema from "@/components/docs/EngineConfigSchemaClient.astro";
The control plane is the Rivet Engine. It reads configuration from files, from environment variables, or from both.
Configuration sources
The engine accepts JSON, JSON5, JSONC, YAML, and YML, plus environment variables.
Environment variables
Prefix a setting with RIVET__ and separate each level of nesting with __. Names are case-insensitive. To set postgres.url:
RIVET__POSTGRES__URL="postgresql://user:password@host:5432/database"Configuration files
Files are discovered automatically in a platform-specific directory:
| Platform | Path |
|---|---|
| Linux | /etc/rivet/config.json |
| macOS | /Library/Application Support/rivet/config.json |
| Windows | C:\ProgramData\rivet\config.json |
Multiple files in the same directory are merged. /etc/rivet/config.json and /etc/rivet/database.json are loaded together.
Override the search path with --config:
# Load from a specific file
rivet-engine --config /path/to/config.json
# Load from a directory
rivet-engine --config /etc/rivet
# Load multiple paths, merged in order
rivet-engine --config /etc/rivet/base.json --config /etc/rivet/override.jsonReference
Envoy load balancing
pegboard.envoy_load_balancer selects how actors are assigned to envoys. The hash strategy uses a hash ring:
{
"pegboard": {
"envoy_load_balancer": {
"hash": {
"virtual_nodes": 8,
"samples": 2,
"max_scan": 16,
"use_snapshot_read": true
}
}
}
}Use samples: 1 for a uniform random pick that skips slot reads, or samples: 2 or higher for power-of-K choices over envoy slot counts. Treat virtual_nodes as an operational invariant once envoys have registered: changing it reshuffles the ring.
Telemetry
The engine exports traces and metrics over OpenTelemetry.
| Variable | Purpose | Default |
|---|---|---|
RIVET_OTEL_ENABLED |
Set to 1 to enable export |
off |
RIVET_OTEL_GRPC_ENDPOINT |
Collector endpoint | http://localhost:4317 |
RIVET_OTEL_SAMPLER_RATIO |
Trace sampling ratio | 0.001 |
Next steps
Storage backends Ports Production checklist
File: src/content/self-host/control-plane/custom.mdx
title: "Custom Platform"
The control plane is a single container image, rivetdev/engine. Any platform that can run it with a persistent volume and a stable address can host it.
Requirements
Your platform must provide:
- A container runtime, or a Linux host you can run the binary on.
- A persistent volume. The control plane is stateful. Storage that resets on redeploy will lose actor state.
- A stable network identity, so workers keep reaching the same place.
- Long-lived connections. Platforms that cap request duration or idle time below an hour will sever WebSockets. This rules out serverless platforms entirely.
SIGTERMplus a grace period, so the control plane can drain and flush state before it is killed.
Steps
openssl rand -hex 32Store it wherever your platform keeps secrets. Without it, the API is unauthenticated.
Pin the tag. Never run latest in production.
docker run -d \
--restart unless-stopped \
-p 6420:6420 \
-p 6421:6421 \
-v rivet-data:/data \
-e RIVET__FILE_SYSTEM__PATH="/data" \
-e RIVET__AUTH__ADMIN_TOKEN="<your-token>" \
rivetdev/engine:2.3.3For multi-node, drop the file system variable, set RIVET__POSTGRES__URL instead, and add NATS for pub/sub. Do not replicate or autoscale a node that is still on the file system backend: RocksDB is single-node. See
6420 carries API, dashboard, and WebSocket traffic and should sit behind TLS. 6421 serves health checks and should stay internal. See
Set idle, read, and send timeouts to at least 3600 seconds on every proxy in the path. The 30 to 60 second defaults sever live WebSockets and cause reconnect storms.
curl -i http://<host>:6421/healthExpect 200. The dashboard is at /ui on port 6420.
A runner config declares a pool that workers connect to. Without one the control plane refuses every worker, so create it before deploying any:
curl -X PUT "http://<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 long-lived workers. See
Point a worker at the control plane and confirm it appears under Runners:
RIVET_ENDPOINT="https://default:<admin-token>@your-control-plane.example.com"Troubleshooting
Workers connect, then drop every 30 to 60 seconds. The load balancer idle timeout is still at its default.
State disappears after a deploy. The volume is not persistent, or the data path does not point at it.
The dashboard rejects your token. RIVET__AUTH__ADMIN_TOKEN is not reaching the container. Confirm the variable name, including the double underscores.
Next steps
Configuration reference Production checklist Deploy your workers
File: src/content/self-host/control-plane/docker-compose.mdx
title: "Docker Compose"
Requirements
- Docker with the Compose plugin
- A host with a persistent volume
Steps
This is a single-node deployment on the file system backend. The named volume is what makes the data survive a restart.
services:
rivet-engine:
image: rivetdev/engine:latest
ports:
- "6420:6420"
volumes:
- rivet-data:/data
environment:
RIVET__FILE_SYSTEM__PATH: "/data"
RIVET__AUTH__ADMIN_TOKEN: "${RIVET_ADMIN_TOKEN}"
restart: unless-stopped
volumes:
rivet-data:echo "RIVET_ADMIN_TOKEN=$(openssl rand -hex 32)" >> .envWithout a token the API is unauthenticated. Keep .env out of version control.
docker compose up -dcurl -i http://localhost:6420/healthExpect 200. The dashboard is at http://localhost:6420/ui; log in with the admin token.
A runner config declares a pool that workers connect to. Without one the control plane refuses every worker, so create it before adding your app:
curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"datacenters":{"default":{"normal":{}}}}'Use normal for the long-lived workers this file runs. See
Add your app to the same Compose file. Inside the Compose network it reaches the control plane at rivet-engine:6420.
my-app:
build: .
environment:
RIVET_ENDPOINT: "http://default:${RIVET_ADMIN_TOKEN}@rivet-engine:6420"
depends_on:
- rivet-engine
restart: unless-stoppeddocker compose up -dConfirm the worker appears under Runners in the dashboard.
Using PostgreSQL
Swap the file system backend for PostgreSQL when you outgrow one node.
services:
postgres:
image: postgres:15
environment:
POSTGRES_DB: rivet
POSTGRES_USER: rivet
POSTGRES_PASSWORD: rivet_password
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rivet -d rivet"]
interval: 5s
timeout: 5s
retries: 12
restart: unless-stopped
rivet-engine:
image: rivetdev/engine:latest
ports:
- "6420:6420"
environment:
RIVET__POSTGRES__URL: postgresql://rivet:rivet_password@postgres:5432/rivet
RIVET__AUTH__ADMIN_TOKEN: "${RIVET_ADMIN_TOKEN}"
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
volumes:
postgres-data:This file still runs a single engine container, so it is not yet a multi-node deployment. It is the prerequisite for one: the file system backend is single-node RocksDB and cannot be shared, while PostgreSQL can. Multi-node deployments also need NATS. See
Using a config file
Mount JSON instead of setting environment variables:
volumes:
- ./rivet-config.json:/etc/rivet/config.json:ro
- rivet-data:/dataSee
Next steps
Production checklist Deploy your workers Configuration reference