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
| Railway | Bex |
|---|---|
builder: DOCKERFILE, or dockerfilePath | Docker runtime (dockerfilePath) |
RAILPACK (the default) or NIXPACKS | A native runtime when startCommand shows one, else Docker |
buildCommand | buildCommand (native runtimes only) |
startCommand | startCommand, or dockerCommand on Docker |
preDeployCommand | preDeployCommand |
healthcheckPath | healthCheckPath |
numReplicas | numInstances |
cronSchedule | Cron job with schedule |
No cronSchedule | Web service; change it to worker if it serves no traffic |
sleepApplication | Explained: only free-tier web services sleep when idle |
requiredMountPath | A disk (paid plan, single instance, choose a size) |
| Postgres or Redis reference variables | A new, empty Bex Postgres or Key Value, wired with fromDatabase or fromService |
| Other variables | One environment group; secrets become sync: false |
environments.<name> overrides | Not mapped: listed per environment, never merged |
watchPatterns, restartPolicyType, region, multiRegionConfig, limitOverride, healthcheckTimeout | Not 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.
[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:
# 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-commitThe converter drafts this render.yaml from them:
# 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 dashboardThe 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.