Migrate from Railway to Bahriya

Railway runs each part of your app as a service, builds from your repository, and stores configuration as variables shared across services. Bahriya uses the same service model but deploys a container image you build, across the regions you choose, with configuration supplied as environment variables and secrets. This guide maps each Railway concept to Bahriya and walks the move.

Updated 3 Aug 20263 min read

Railway runs each part of your app as a service, builds from your repository, and stores configuration as variables shared across services. Bahriya uses the same service model but deploys a container image you build, across the regions you choose, with configuration supplied as environment variables and secrets. This guide maps each Railway concept to Bahriya and walks the move.

What maps to what

RailwayBahriya
ProjectA project
ServiceA container — HTTP, worker, or cron job
Build from repo (Nixpacks) / DockerfileA Dockerfile producing an image
VariablesEnvironment variables and secrets
Shared variablesSecrets attached to the project
Database / Redis pluginAn external database or Bahriya Memcached via a connection secret
PORT variableThe port you declare with --port
ReplicasReplicas and autoscaling
Generated domain + custom domainsDefault hostname with automatic TLS + custom domains

1. Containerise

If your Railway service already uses a Dockerfile, carry it over. If it builds from source, write a Dockerfile that installs dependencies and starts your app on a single port:

FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]

Railway injects a PORT variable and expects your app to bind to it. Keep reading the port from the environment and bind to 0.0.0.0, then declare it with --port on Bahriya (default 8080). Build and push the image:

docker build -t ghcr.io/myorg/api:v1.0.0 .
docker push ghcr.io/myorg/api:v1.0.0

2. Variables to env and secrets

Railway keeps configuration in service and shared variables. On Bahriya, split them:

  • Non-sensitive values become plain environment variables, visible in the Console.
  • Sensitive values (database URLs, API keys, tokens) become secrets — encrypted at rest, injected as env vars, and wired to the variable name your app already reads.
Note

Railway lets services reference each other's variables (for example a database URL exposed to the app automatically). On Bahriya those links are explicit: take the connection string from your database and store it as a secret, then wire it into the app container yourself.

3. Plugins and databases

Railway plugins provision a managed database or cache and expose a connection URL as a variable. On Bahriya:

  • Database. Run your database with a managed provider or your own instance, reachable over the network. Store the connection string as a secret wired to DATABASE_URL. Provision, migrate your data, and verify connectivity from a Bahriya container before cutting over.
  • Cache. For a Memcached-compatible cache, create a Bahriya Memcached instance. For Redis or another engine, connect to an external instance via a secret.

4. Scaling

Railway scales services by replica count and resource limits. On Bahriya, set minimum CPU (millicores) and memory (MB), a replica count, and — for automatic scaling — a maximum replica count. Setting --max_replicas above --replicas enables autoscaling. See Resources and Scaling.

5. Deploy

From the Console follow Deploy Your First Container, or with Reis:

reis container:create \
  --type http \
  --name "Web API" \
  --handle web-api \
  --image ghcr.io/myorg/api:v1.0.0 \
  --registry ghcr \
  --port 8080 \
  --healthcheck /healthz \
  --replicas 2 \
  --max_replicas 8 \
  --regions falkenstein-1 \
  --regions virginia-1 \
  --env NODE_ENV=production \
  --secrets DATABASE_URL=database-url

Deploy a background service the same way with --type worker, and a scheduled task with --type cronjob. Choose regions close to your users — see Regions.

6. Point your domain and cut over

Your container is reachable at https://web-api.<region>.x.on.bahriya.app with automatic TLS. Test there, then add your production domain, verify it, and move DNS across — see Custom Domain Setup. Keep the Railway service running until traffic has drained, then remove it.