website/src/content/cookbook/vpc-air-gapped.mdx
Patterns for running self-hosted Rivet inside a private network: a VPC without internet egress, an on-premises rack, or a fully air-gapped environment. The engine is one service, the recommended single-node storage backend is the local file system, and the engine makes no outbound connections by default. Self-hosting is the only Rivet deployment model that supports air-gapped networks; see the Self-Hosting Overview for the full comparison with BYOC.
A self-hosted deployment has three components, all of which live inside your network:
| Component | Role | Inside the perimeter |
|---|---|---|
| Your backend | Your application server, including the runner that executes actor code | Yes |
| Rivet Engine | Orchestration service that manages actor lifecycle, routes messages, and serves the dashboard and APIs | Yes |
| Storage | Persistence for actor state. Local file system for single-node, PostgreSQL or FoundationDB for multi-node | Yes |
There is no license server, no Rivet Cloud account, and no callback to rivet.dev. Clients inside the perimeter reach actors through the engine's gateway over your private network. See Architecture.
The engine compiles to a single rivet-engine binary. Build it from source outside the perimeter, then copy the binary across the boundary:
git clone https://github.com/rivet-dev/rivet.git
cd rivet
cargo build --release -p rivet-engine
# Copy target/release/rivet-engine into the perimeter.
Prebuilt binaries are coming soon; see Installing Rivet Engine for current options.
Run it with the file system backend, which stores everything on local disk and is the production-ready choice for single-node deployments. The File System docs list air-gapped environments as a primary use case because it needs no database infrastructure:
RIVET__database__file_system__path="/var/lib/rivet/data" ./rivet-engine
Configuration can also come from files. The engine discovers config at /etc/rivet/config.json on Linux (JSON, JSON5, JSONC, YAML, and YML are all supported), and --config overrides the path. Environment variables use the RIVET__ prefix with __ as the separator. See Configuration.
The engine serves its own dashboard on port 6420, so inspection and namespace management work with nothing but a browser inside the perimeter.
For Docker hosts without registry access, move the engine image across the boundary the standard way:
# Outside the perimeter.
docker pull rivetdev/engine:latest
docker save rivetdev/engine:latest -o rivet-engine.tar
# Inside the perimeter.
docker load -i rivet-engine.tar
Then run the engine and your app together in one Compose file:
services:
rivet-engine:
image: rivetdev/engine:latest
ports:
- "6420:6420"
volumes:
- rivet-data:/data
environment:
RIVET__FILE_SYSTEM__PATH: "/data"
restart: unless-stopped
my-app:
build: .
environment:
RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420"
depends_on:
- rivet-engine
restart: unless-stopped
volumes:
rivet-data:
RIVET_ENDPOINT uses the format http://namespace:token@host:port and tells your app to connect to the engine as a runner instead of running standalone. After both services start, register your runner with the engine through the dashboard or its API. The full walkthrough, including PostgreSQL setup for multi-node deployments, is in Docker Compose.
The engine exports traces and metrics only when you opt in with OpenTelemetry. Export is disabled unless RIVET_OTEL_ENABLED=1 is set, and the export target defaults to a local collector at http://localhost:4317. With no configuration, nothing crosses the perimeter.
When you want observability, keep it inside the network:
RIVET_OTEL_ENABLED=1 and point RIVET_OTEL_GRPC_ENDPOINT at a collector you run inside the perimeter.RIVET_OTEL_SAMPLER_RATIO to control trace sampling.See the Production Checklist for monitoring guidance.
If you ship software that runs inside your customers' VPCs, the same setup turns Rivet into an internal component of your product rather than a service your customers must reach over the internet:
rivetdev/engine to the Compose file or chart you already deliver. Your app finds it over the private network via RIVET_ENDPOINT, so one artifact deploys the whole stack.http://namespace:token@host:port), so a single image works across customer deployments. See Endpoints.RIVET_PUBLIC_ENDPOINT or anywhere clients can read it.RIVET_PUBLIC_ENDPOINT with a public (pk_) token is only required when browser clients connect to actors in serverless runtime mode. Backend-only deployments can skip it entirely.| Backend | Use when | Status |
|---|---|---|
| File System (RocksDB-based) | Single-node deployments, including air-gapped installs | Production-ready, single node only |
| PostgreSQL | Multi-node deployments | Recommended for multi-node today, but experimental |
| FoundationDB | Largest production deployments | Enterprise |
For multi-node deployments, run two or more engine nodes behind a load balancer and add NATS for pub/sub, which replaces the default PostgreSQL LISTEN/NOTIFY path at high throughput. Neither is needed for a single-node file system install. See the Production Checklist.
6420 reachable only from inside the perimeter unless clients outside it genuinely need access.