# Kubernetes > Source: `src/content/docs/self-hosting/kubernetes.mdx` > Canonical URL: https://rivet.dev/docs/self-hosting/kubernetes > Description: Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage. --- PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. ## Prerequisites - Kubernetes cluster - `kubectl` configured - [Metrics server](https://github.com/kubernetes-sigs/metrics-server) (required for HPA) — included by default in most distributions (k3d, GKE, EKS, AKS) ## Deploy Rivet Engine ### Download Manifests Download the `self-host/k8s/engine` directory from the Rivet repository: ```bash npx giget@latest gh:rivet-dev/rivet/self-host/k8s/engine rivet-k8s cd rivet-k8s ``` ### Configure Engine In `02-engine-configmap.yaml`, set `public_url` to your engine's external URL. ### Configure PostgreSQL In `11-postgres-secret.yaml`, update the PostgreSQL password. See [Using a Managed PostgreSQL Service](#using-a-managed-postgresql-service) for external databases. ### Configure Admin Token Generate a secure admin token and save it somewhere safe: ```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 ``` ### Deploy ```bash # Apply all manifests kubectl apply -f . # 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 ``` ### Access the Engine Visit `/ui` on your `public_url` to access the dashboard. ## Connecting Your Project ### Create your server Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. ### Create Kubernetes manifests Create these manifest files for your app: ```yaml deployment.yaml 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 ``` ```yaml service.yaml apiVersion: v1 kind: Service metadata: name: rivetkit-app namespace: your-namespace spec: selector: app: rivetkit-app ports: - name: http port: 8080 targetPort: 8080 ``` ### Configure the endpoint 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](/docs/general/endpoints) for all options. ```yaml apiVersion: v1 kind: Secret metadata: name: rivetkit-secrets namespace: your-namespace type: Opaque stringData: RIVET_ENDPOINT: http://my-app:your-admin-token@your-engine.example.com ``` ### Deploy your app ```bash kubectl apply -f rivetkit-secrets.yaml kubectl apply -f deployment.yaml kubectl apply -f service.yaml ``` ### Register your runner with the engine ### Dashboard 1. Open the Rivet Engine dashboard in your browser. 2. Enter your admin token when prompted. 3. In the namespace sidebar, click **Settings**. 4. Click **Add Provider**, then choose **Custom**. 5. Click **Next**. 6. Go to **Confirm Connection**, enter your app endpoint (e.g. `http://rivetkit-app.your-namespace:8080/api/rivet`), then click **Add**. ### CLI (curl) Register your runner programmatically via the engine API: ```bash @nocheck 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": {} } } }' ``` ## Advanced ### Using a Managed PostgreSQL Service If you prefer to use a managed PostgreSQL service (e.g. Amazon RDS, Cloud SQL, Azure Database) instead of the bundled Postgres deployment: - Update the `postgres.url` connection string in `02-engine-configmap.yaml` to point to your managed instance - Delete the bundled PostgreSQL manifests: - `10-postgres-configmap.yaml` - `11-postgres-secret.yaml` - `12-postgres-statefulset.yaml` - `13-postgres-service.yaml` ### Applying Configuration Updates When making subsequent changes to `02-engine-configmap.yaml`, restart the engine pods to pick up the new configuration: ```bash kubectl apply -f 02-engine-configmap.yaml kubectl -n rivet-engine rollout restart deployment/rivet-engine ``` ### Ingress and Load Balancer Timeouts 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): ```yaml nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" ``` - **AWS Load Balancer Controller (ALB)**: ```yaml 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. ## Next Steps - Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live - See [Configuration](/docs/self-hosting/configuration) for all engine config options _Source doc path: /docs/self-hosting/kubernetes_