Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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.1 or 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-railway

Save 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 Railway

Create 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 open

When linking, select rflow-example, the intended environment, and the rflow service. Before the first railway up, configure the rflow service's Settings:

SettingValue
BuildUse the root Dockerfile, which Railway detects automatically.
Custom Start CommandLeave empty to use the image's command.
Replicas1 in one region.
Healthcheck Path/live.
Healthcheck Timeout600 seconds.
Restart PolicyOn Failure, maximum 10 restarts.
Draining Time120 seconds.
Serverless / app sleepingDisabled. 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 postgres

In the rflow service's Variables tab, set:

VariableValue
DATABASE_URLYour database connection string, with sslmode=require.
RFLOW_API_TOKENA long random token, for example one generated with openssl rand -hex 32.
PORT3940, 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 rflow

Check 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/workflows

Export 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 rflow

Wait until the old deployment has fully stopped, then apply any variable changes and deploy the replacement:

railway up --service rflow
railway logs --service rflow

Follow 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.