---
title: "Railway Deployment"
description: "Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure."
skill: true
---

import { TutorialVideo } from '@/components/TutorialVideo';



## Video Tutorial

<TutorialVideo videoId="zk8ODYz8CrI" />

<Warning>
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.
</Warning>

## Quick Deploy

Choose the template that best fits your needs:

| **Rivet Template** | **Rivet Starter** |
|-------------------|-------------------|
| [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/rivet?referralCode=RC7bza&utm_medium=integration&utm_source=template&utm_campaign=generic) | [![Deploy Rivet Starter](https://railway.com/button.svg)](https://railway.com/deploy/rivet-starter) |
| **Blank template** to start fresh | **Complete example** with chat app |
| - Rivet Engine | - Pre-configured Rivet Engine |
| - PostgreSQL database | - Example chat application with Actors |
| - Basic configuration | - PostgreSQL database |
| - Manual setup required | - Rivet Inspector for debugging |
| | - Ready to run immediately |

You can also use the [Rivet Railway template](https://github.com/rivet-dev/template-railway) as a starting point for your application.

<Note>
After deploying either template, you can find the `RIVET__AUTH__ADMIN_TOKEN` under the **Variables** tab in the Railway dashboard. This token is required to access the Rivet Inspector.
</Note>

## Manual Deployment

### Prerequisites

1. [Railway account](https://railway.app)
2. [Railway CLI](https://docs.railway.app/develop/cli) (optional)

### Step 1: Create New Project

```bash
# Using Railway CLI
railway init

# Or create via dashboard
# https://railway.app/new
```

### Step 2: Add Services

#### Deploy PostgreSQL Database

1. Click "New Service" → "Database" → "PostgreSQL"
2. Railway automatically provisions and configures PostgreSQL
3. Note the connection string from the service variables

#### Deploy Rivet Engine

1. Click "New Service" → "Docker Image"
2. Set image: `rivetdev/engine:latest`
3. Configure environment variables:
    - `RIVET__POSTGRES__URL=${{Postgres.DATABASE_URL}}`
4. Configure graceful shutdown (see [Graceful Shutdown](#graceful-shutdown) below)

### Step 3: Deploy Your Application

Follow the [Railway Quick Start guide](https://docs.railway.com/quick-start) to deploy your repository:

1. Connect your GitHub account to Railway
2. Select your repository containing your Rivet application
3. Railway will automatically detect and deploy your application
4. Configure environment variables for your application:
    - `RIVET_ENDPOINT=${{Rivet.RAILWAY_PRIVATE_DOMAIN}}` - Points to the Rivet Engine service's private domain

## WebSocket Timeouts

Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Railway's HTTP proxy supports WebSockets, but you must make sure no app-side timeout cuts them off.

If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Railway service hosting your RivetKit app, since envoys connect to it over WebSocket.

## Graceful Shutdown

By default, Railway kills the old deploy 0 seconds after sending `SIGTERM` (see [Railway's docs](https://docs.railway.com/deployments/reference#singleton-deploys)), so in-flight requests are dropped and state flushes can be interrupted on every deploy. Rivet ships with a `SIGTERM` handler that drains cleanly, but it only gets to run if Railway is configured to give it time.

Configure the following under **Settings → Deploy** on your service:

- **Draining seconds** — the grace window between `SIGTERM` and `SIGKILL`. This is how long Rivet has to finish in-flight work and flush state. See [Railway's deployment teardown docs](https://docs.railway.com/deployments/deployment-teardown).

Set draining seconds in the dashboard, via `drainingSeconds` in config-as-code, or via the `RAILWAY_DEPLOYMENT_DRAINING_SECONDS` service variable. A reasonable value is **60 seconds**.

If your start command is a wrapper process (for example `npm start`, `yarn start`, `pnpm start`, or a shell script), the wrapper becomes PID 1 and swallows the signal — your app never drains and Railway force-kills it at the end of the window.

- Invoke your binary directly as the start command (for example `node dist/index.js`, not `npm start`).
- Or run `dumb-init` / `tini` as PID 1 in your Dockerfile so signals forward to your process.   

## Next Steps

- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live
- See [Configuration](/docs/self-hosting/configuration) for all options
