Skip to main content

Migrate from Railway

Map a Railway railway.json or railway.toml service to a Bex render.yaml, recreate its Postgres and Redis references, and see which Railway settings have no Bex equivalent.

Share
Last updated on October 5, 2026
On this page

Railway's config as code file, railway.json or railway.toml, configures the build and deploy of one service. A Bex render.yaml describes managed services, datastores and disks. Most of a Railway file maps, but regions, resource limits, restart policies and environment overrides do not, and moving a file moves no data.

Railway now marks config as code deprecated in favour of infrastructure as code, and says existing files stop being read on 2026-12-01 (both checked 2026-10-01). The converter reads the documented config-as-code keys, which are what a repository holds today.

Bex is in active development and its upstream project does not yet recommend production workloads. Begin with a non-production rehearsal and check compatibility and required capabilities before proceeding. Compare costs with Railway pricing and Bex pricing directly; this guide does not restate either.

Use the converter​

Paste one service's railway.json or railway.toml, and optionally its variables, into the Railway converter. It is a browser starting point: it drafts a render.yaml by the rules below, lists every element it could not map with the reason, and checks the draft with the render.yaml checker. bex blueprints validate remains the authoritative check. Nothing you paste leaves your browser. Convert each service of a Railway project separately.

How Railway maps to Bex​

RailwayBex
builder: DOCKERFILE, or dockerfilePathDocker runtime (dockerfilePath)
RAILPACK (the default) or NIXPACKSA native runtime when startCommand shows one, else Docker
buildCommandbuildCommand (native runtimes only)
startCommandstartCommand, or dockerCommand on Docker
preDeployCommandpreDeployCommand
healthcheckPathhealthCheckPath
numReplicasnumInstances
cronScheduleCron job with schedule
No cronScheduleWeb service; change it to worker if it serves no traffic
sleepApplicationExplained: only free-tier web services sleep when idle
requiredMountPathA disk (paid plan, single instance, choose a size)
Postgres or Redis reference variablesA new, empty Bex Postgres or Key Value, wired with fromDatabase or fromService
Other variablesOne environment group; secrets become sync: false
environments.<name> overridesNot mapped: listed per environment, never merged
watchPatterns, restartPolicyType, region, multiRegionConfig, limitOverride, healthcheckTimeoutNot mapped, with the reason

Example​

This is the converter page's sample: a Bun service built with Railpack that uses a Railway Postgres and Redis.

toml
[build]
builder = "RAILPACK"
 
[deploy]
startCommand = "bun run src/index.ts"
healthcheckPath = "/"
restartPolicyType = "ALWAYS"

Its variables in KEY=VALUE form, as railway variable list --kv prints them, with references as written in Railway:

text
# railway variables --kv, with references left as written
DATABASE_URL=${{Postgres.DATABASE_URL}}
PGHOST=${{Postgres.PGHOST}}
PGPORT=${{Postgres.PGPORT}}
REDIS_URL=${{Redis.REDIS_URL}}
MONGO_URL=${{MongoDB.MONGO_URL}}
SHARED_FLAG=${{shared.FEATURE_FLAG}}
PUBLIC_URL=https://${{RAILWAY_PUBLIC_DOMAIN}}
RAILWAY_DOCKERFILE_PATH=Dockerfile
NODE_ENV=production
PORT=8080
STRIPE_SECRET_KEY=replace-me-do-not-commit

The converter drafts this render.yaml from them:

yaml
# Draft render.yaml for Bex, converted from Railway by bex.co/tools.
# A starting point, not a migration: replace the placeholders, enter secrets
# in the dashboard, choose plans, and move data, domains and add-on
# credentials separately. Then run `bex blueprints validate`.
services:
  # From railway.toml: railway.toml
  # plan: not set; choose one at https://bex.co/pricing
  - type: web
    name: app
    runtime: docker
    repo: https://github.com/YOUR-ORG/YOUR-REPO  # replace with your repository
    branch: YOUR-BRANCH  # replace with your deploy branch
    dockerCommand: bun run src/index.ts
    healthCheckPath: /
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: app-db
          property: connectionString
      - key: PGHOST
        fromDatabase:
          name: app-db
          property: host
      - key: PGPORT
        fromDatabase:
          name: app-db
          property: port
      - key: REDIS_URL
        fromService:
          name: app-cache
          type: keyvalue
          property: connectionString
      - fromGroup: app-config
  # From .env line 5: REDIS_URL
  # plan: not set; choose one at https://bex.co/pricing
  - type: keyvalue
    name: app-cache
    ipAllowList: []
databases:
  # From .env line 2: DATABASE_URL
  # plan: not set; choose one at https://bex.co/pricing
  - name: app-db
envVarGroups:
  # From .env: variables
  - name: app-config
    envVars:
      - key: NODE_ENV
        value: production
      - key: STRIPE_SECRET_KEY
        sync: false  # set this secret in the dashboard

The Postgres and Redis references become a new, empty app-db database and app-cache Key Value store. Rename app to your service's name, replace the repository and branch placeholders, choose plans, and enter STRIPE_SECRET_KEY before you validate it. No native runtime can be inferred from a bun command, so the draft builds a Dockerfile. The converter lists the Railpack builder, the restart policy, the MongoDB, shared and domain references, and RAILWAY_DOCKERFILE_PATH as not mapped, each with its reason.

Builds and commands​

Railpack is Railway's default builder, and DOCKERFILE builds a Dockerfile (config as code, checked 2026-10-01). A Railpack or Nixpacks build has no Bex equivalent, so the draft uses a native runtime only when startCommand shows one; otherwise it falls back to the Docker runtime and you add a Dockerfile (Docker deploys). preDeployCommand becomes Bex's pre-deploy command, which runs in the new image without the service's disk.

A Railway file does not say whether the service takes public traffic, so the draft makes it a web service listening on the PORT Bex injects. Change it to a background worker if it serves no requests.

Cron jobs​

A cronSchedule becomes a Bex cron job with the same five-field schedule. Railway schedules in UTC (Railway cron jobs, checked 2026-10-01); Bex sets no per-job timezone, so confirm it with your instance operator. Bex cron jobs take no pre-deploy command, disk or instance count, so the converter lists those keys as not mapped.

Variables and datastores​

Variables are not in the Railway file. A reference such as ${{Postgres.DATABASE_URL}} names another service's variable (Railway variables, checked 2026-10-01). For Postgres and Redis references the draft provisions a new, empty Bex Postgres or Key Value store and wires it with fromDatabase or fromService; it never copies a connection string. Other references cannot be resolved in the browser, so resolve them yourself. Enter secret values through Bex's secrets interface.

Volumes and disks​

Railway allows one volume per service and no replicas with a volume (Railway volumes, checked 2026-10-01). A requiredMountPath becomes a persistent disk on a paid plan with a single instance; choose a size from 10 to 10,000 GB. The disk starts empty.

What does not map​

sleepApplication has no render.yaml field: on Bex only free-tier web services sleep when idle (idle services). Regions, resource limits, restart policies, watch patterns and health-check timeouts have no render.yaml setting. environments.<name> overrides are listed per environment and never merged into the draft; recreate per-environment differences with Bex projects and environments.

Move data and traffic​

Rehearse the data transfer and the cutover with steps 4 and 5 of Migrate from Render; they apply to any source host. Stop every source writer, including cron services, before the final transfer. Add your domains through Bex domain setup and use the DNS records it returns.

See Bex vs Railway for a product comparison.

Was this page helpful?

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex