Docker Compose
Run Rivet nodes with Docker Compose.
Your container runs as a node: it opens a connection out to the control plane on startup. It needs no published port.
Requirements
A namespace and a node pool created with PUT /pools/{pool} (see Nodes & Node Pools). In local development, RivetKit creates the default pool for you.
- Docker with the Compose plugin
- A running control plane
Steps
Package your app
FROM node:24-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]
CMD invokes the binary directly rather than npm start, so the process receives SIGTERM and can drain.
Rivet requires Node.js 22 or newer.
Add the service
stop_grace_period matters here. Each Rivet Actor gets up to 15 seconds to save its state, and Compose’s default of 10 seconds can cut them off.
services:
my-app:
build: .
environment:
RIVET_ENDPOINT: "${RIVET_ENDPOINT}"
stop_grace_period: 60s
restart: unless-stopped
The endpoint value is described in Deploy Nodes. Put it in a .env file next to the Compose file and keep it out of version control.
Run alongside a self-hosted control plane
When both halves live in the same Compose file, the node reaches the control plane by service name over the Compose network:
my-app:
build: .
environment:
RIVET_ENDPOINT: "http://default:${RIVET_TOKEN}@rivet:6420"
depends_on:
- rivet
stop_grace_period: 60s
restart: unless-stopped
See the control plane guide for the other half of this file.
Start it
docker compose up -d
Verify
docker compose ps
The service should be running. Call one of your Rivet Actors to confirm it responds.
Scaling
Compose has no autoscaler. Nodes are interchangeable, so docker compose up -d --scale my-app=3 adds replicas that all join the same pool, but they share one host’s CPU and memory. For capacity beyond a single host, or for autoscaling, move to Kubernetes or ECS.
Keep stop_grace_period: 60s on every replica. Scaling down cuts actors off otherwise.