website/src/content/docs/self-hosting/kubernetes.mdx
kubectl configured```bash
npx giget@latest gh:rivet-dev/rivet/self-host/k8s/engine rivet-k8s
cd rivet-k8s
```
```bash
openssl rand -hex 32
```
Create the namespace and store the token as a Kubernetes secret:
```bash
kubectl create namespace rivet-engine
kubectl -n rivet-engine create secret generic rivet-secrets --from-literal=admin-token=YOUR_TOKEN_HERE
```
# Wait for all pods to be ready
kubectl -n rivet-engine wait --for=condition=ready pod -l app=nats --timeout=300s
kubectl -n rivet-engine wait --for=condition=ready pod -l app=postgres --timeout=300s
kubectl -n rivet-engine wait --for=condition=ready pod -l app=rivet-engine --timeout=300s
# Verify all pods are running
kubectl -n rivet-engine get pods
```
Follow the quickstart to create a working server with actors.
</Step> <Step title="Create Kubernetes manifests">Create these manifest files for your app:
<CodeGroup>apiVersion: apps/v1
kind: Deployment
metadata:
name: rivetkit-app
namespace: your-namespace
spec:
replicas: 1
selector:
matchLabels:
app: rivetkit-app
template:
metadata:
labels:
app: rivetkit-app
spec:
# Allow enough time for actors to gracefully stop on SIGTERM.
# The runner waits up to 30m for actors to finish.
# Add buffer for runner shutdown overhead after actors stop.
# See: /docs/actors/versions#graceful-shutdown-sigterm
terminationGracePeriodSeconds: 2100
containers:
- name: rivetkit-app
image: registry.example.com/your-team/rivetkit-app:latest
envFrom:
- secretRef:
name: rivetkit-secrets
apiVersion: v1
kind: Service
metadata:
name: rivetkit-app
namespace: your-namespace
spec:
selector:
app: rivetkit-app
ports:
- name: http
port: 8080
targetPort: 8080
Create rivetkit-secrets.yaml with RIVET_ENDPOINT pointing to the engine. This tells your app to connect as a runner instead of running standalone. See Endpoints for all options.
apiVersion: v1
kind: Secret
metadata:
name: rivetkit-secrets
namespace: your-namespace
type: Opaque
stringData:
RIVET_ENDPOINT: http://my-app:[email protected]
kubectl apply -f rivetkit-secrets.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
http://rivetkit-app.your-namespace:8080/api/rivet), then click Add.Register your runner programmatically via the engine API:
curl -X PUT "https://your-engine.example.com/runner-configs/default?namespace=default" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-admin-token" \
-d '{
"datacenters": {
"default": {
"normal": {}
}
}
}'
If you prefer to use a managed PostgreSQL service (e.g. Amazon RDS, Cloud SQL, Azure Database) instead of the bundled Postgres deployment:
postgres.url connection string in 02-engine-configmap.yaml to point to your managed instance10-postgres-configmap.yaml11-postgres-secret.yaml12-postgres-statefulset.yaml13-postgres-service.yamlWhen making subsequent changes to 02-engine-configmap.yaml, restart the engine pods to pick up the new configuration:
kubectl apply -f 02-engine-configmap.yaml
kubectl -n rivet-engine rollout restart deployment/rivet-engine
Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms.
Raise the idle / read / send timeout on every Ingress and load balancer in front of the engine to at least 1 hour (3600 seconds). Examples:
NGINX Ingress (annotations on the Ingress):
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
AWS Load Balancer Controller (ALB):
alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600
GCE Ingress (GKE): set timeoutSec: 3600 on the BackendConfig referenced by the Service.
The same guidance applies to the load balancer fronting your RivetKit app, since envoys connect to it over WebSocket.