Railway
Run the Rivet control plane on Railway.
Requirements
- A Railway account
- The Railway CLI, optional
Steps
Deploy a template
Two templates are published. Both provision the control plane and a PostgreSQL database.
| Rivet | Rivet Starter |
|---|---|
| Blank template | Complete example |
| Control plane and PostgreSQL | Control plane, PostgreSQL, and a chat app built with actors |
| Manual setup from here | Runs immediately |
To build it by hand instead, create a project, add a PostgreSQL database, then add a Docker Image service using rivetdev/engine:latest with RIVET__POSTGRES__URL=${{Postgres.DATABASE_URL}}.
The Railway template repository is a useful starting point for your own application.
Find the admin token
The templates generate one. It is under the Variables tab as RIVET__AUTH__ADMIN_TOKEN.
Set the draining period
Railway kills the old deploy 0 seconds after SIGTERM by default, so in-flight requests are dropped and state flushes are interrupted on every single deploy. Rivet drains cleanly on SIGTERM, but only if it is given time.
Under Settings → Deploy, set Draining seconds to 60. It can also be set with drainingSeconds in config-as-code, or the RAILWAY_DEPLOYMENT_DRAINING_SECONDS service variable. See Railway’s deployment teardown docs.
Verify
Open your service URL with /ui appended and log in with the admin token.
Create the runner config
A runner config declares a pool that workers connect to. Without one the control plane refuses every worker, so create it before deploying any:
curl -X PUT "https://<your-service>.up.railway.app/runner-configs/default?namespace=default" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"datacenters":{"default":{"normal":{}}}}'
Use normal for long-lived workers. See Runner configs.
Signal handling
If your start command is a wrapper (npm start, yarn start, a shell script), the wrapper becomes PID 1, swallows SIGTERM, and the drain never happens no matter what you set the draining period to.
- Invoke the binary directly, for example
node dist/index.js. - Or run
dumb-initortinias PID 1 in your Dockerfile.
WebSocket timeouts
Railway’s HTTP proxy supports WebSockets. If you add your own reverse proxy inside the service, raise its idle and read timeouts to 3600. See Ports.
Scaling past one instance
Both templates provision a single control plane service on PostgreSQL, which is a fine starting point. Before raising the replica count, add NATS for pub/sub so replicas coordinate. See Storage.
Railway has no CPU-target autoscaler, so replica count is a number you set rather than one that moves with load. Pick it for peak traffic, and use Kubernetes or AWS ECS if you need it to scale on its own.