Railway
Deploy rflow as an always-on Railway service with a Postgres database. This guide starts with a heartbeat workflow so you can verify the deployment without RPC credentials or a funded wallet, then replace it with your own project.
Prerequisites
- A Railway account and the Railway CLI.
- Docker, to inspect the release image and optionally test it locally.
- A reviewed rflow image from release
0.2.1or later. Use its published digest from the release workflow; see release and preflight. - Postgres 14+ with a TLS endpoint reachable from Railway, a trusted certificate, and a database dedicated to this rflow project.
1. Prepare the project
Create a directory for your deployment:
mkdir rflow-railway
cd rflow-railwaySave this as rflow.yaml:
rflow_version: 1
name: railway-heartbeat
config:
db_connection: ${DATABASE_URL}
port: 3940
server:
bind: 0.0.0.0:3940
auth:
token: ${RFLOW_API_TOKEN}
notifications:
channels:
ops:
console: {}
workflows:
heartbeat:
trigger:
interval:
every: 1m
steps:
- id: heartbeat
notify:
channel: ops
message: rflow is running on RailwayCreate a Dockerfile. Replace REPLACE_WITH_REVIEWED_DIGEST with the 64-character
SHA-256 value of your release image:
FROM ghcr.io/joshstevens19/rflow@sha256:REPLACE_WITH_REVIEWED_DIGEST
COPY --chown=1000:1000 rflow.yaml /app/project/rflow.yaml
CMD ["start", "--path", "/app/project"]The base image runs as a non-root user and already supplies the rflow entrypoint.
Leave Railway's custom Start Command empty so it uses this image's command.
For your own project, explicitly copy every required ABI and script, for example
COPY --chown=1000:1000 abis/ /app/project/abis/. The project directory must stay
writable because rflow generates embedded-engine configuration at startup.
Add .env, .env.*, and any credential files to both .gitignore and
.dockerignore. Inject secrets through Railway variables, never through the image.
This guide configures the service through the Railway dashboard. Do not add a
railway.json or railway.toml: Railway's legacy
Config as Code is unavailable for new
services, and support for existing users ends on December 1, 2026. For a versioned
infrastructure definition, use Railway's current
Infrastructure as Code.
2. Create the Railway services
railway login
railway init --name rflow-example
railway add --service rflow
railway link
railway openWhen linking, select rflow-example, the intended environment, and the rflow
service. Before the first railway up, configure the rflow service's Settings:
| Setting | Value |
|---|---|
| Build | Use the root Dockerfile, which Railway detects automatically. |
| Custom Start Command | Leave empty to use the image's command. |
| Replicas | 1 in one region. |
| Healthcheck Path | /live. |
| Healthcheck Timeout | 600 seconds. |
| Restart Policy | On Failure, maximum 10 restarts. |
| Draining Time | 120 seconds. |
| Serverless / app sleeping | Disabled. Workflows must run without incoming HTTP traffic. |
The health check, restart policy, and draining time are service settings. Save them before deploying and confirm the effective settings in the deployment details. If migrating an existing service, remove any legacy config-file overrides before relying on dashboard values. Keep the stop-first update procedure below; a draining time does not prevent two deployments from overlapping during startup.
Use an existing Postgres endpoint, or add Railway Postgres:
railway add --database postgresIn the rflow service's Variables tab, set:
| Variable | Value |
|---|---|
DATABASE_URL | Your database connection string, with sslmode=require. |
RFLOW_API_TOKEN | A long random token, for example one generated with openssl rand -hex 32. |
PORT | 3940, matching the bind address in rflow.yaml. |
For a Railway database named Postgres, the variable reference is
${{Postgres.DATABASE_URL}}?sslmode=require when the referenced URL has no query
parameters. If it already has options, append &sslmode=require instead.
Use the database's private endpoint for communication within the Railway project.
The heartbeat only needs the database and API token. Add your RPC URLs, notification credentials, and signer configuration when switching to your own workflow. All execution state lives in Postgres; the application service does not need a persistent volume for the heartbeat. Retained script artifacts need separate storage.
3. Deploy and verify
railway up --service rflow
railway logs --service rflowCheck that the service starts, connects to Postgres, and emits the heartbeat.
Check the build log confirms the Dockerfile was used and the deployment details
show one replica, /live, the 600-second timeout, and the 120-second drain.
For browser access or webhook delivery, open Settings → Networking, generate
a domain, and set its target port to 3940. Railway terminates HTTPS; rflow serves
HTTP inside the container.
curl --fail https://YOUR-RAILWAY-DOMAIN/live
curl --fail https://YOUR-RAILWAY-DOMAIN/health
curl --fail -H "Authorization: Bearer $RFLOW_API_TOKEN" \
https://YOUR-RAILWAY-DOMAIN/api/workflowsExport the same API token in your local terminal before the authenticated request.
The viewer at / prompts for the token. /live and /health are public by
default; /metrics requires the bearer token. Webhook routes need their own
HMAC authentication.
Railway's deployment health check
only gates startup. Add ongoing monitoring of /health: 200 means healthy and
503 means degraded, with details in reasons. Use metrics and alerting
to detect stalled workflows and failed RPCs after deployment.
4. Update the service
Keep one replica and use a maintenance window for updates. Disable GitHub autodeploys if you connect a repository. Back up Postgres, validate the new config, and retain the previous image digest and project files. Then remove the current application deployment:
railway down --service rflowWait until the old deployment has fully stopped, then apply any variable changes and deploy the replacement:
railway up --service rflow
railway logs --service rflowFollow the same stop-first procedure for configuration or secret changes that
trigger a redeploy. The draining period
gives rflow time to shut down. Expect downtime and recheck /health afterwards.
Keep the project name unchanged when reusing its database. Missed cron/interval
ticks require explicit catch_up configuration; see reliability.
For your own project, run rflow validate --preflight --path . and
rflow doctor --path . with the intended environment before deployment. See the
production runbook for backup, recovery, and rollback.
5. Clean up
Use railway down --service rflow to stop the application. This keeps the service
and database. When finished with the example, delete the unused services/project
in the dashboard to stop their resource charges. Export a database backup before
deleting Postgres or its volume.