Move an App From Railway: Services, Networking, and Data

·5 min read·Updated
migrationrailwaydeploymentarchitecturehow-to

An individual Railway service can become an application here. Its environment variables and persistent data need to be moved separately. A multi-service project takes more planning: this platform has no private network, reference variables, managed cron service or replicas. Inventory those dependencies before moving traffic.

The mapping

Railway Here
Project No equivalent — apps aren't grouped
Service Application
Service variables Environment variables on the app
Variable references between services Not available — set values manually
Private networking (.railway.internal) Not available — public endpoint + credentials
Volume Volume (also single-service)
Railpack auto-build Dockerfile for Git push, or ZIP build detection for a supported project
Generated domain Generated HTTPS hostname
Replicas Single container per app
Cron / scheduled jobs Scheduling inside your own process

The rows for networking, references, cron and replicas can change how your app is built. Railway's networking, variables and Railpack docs describe the current source-platform behavior.

Private networking is the big one

Railway services in one project can address each other as SERVICE_NAME.railway.internal over a private network.

There's no equivalent here. Every port binding gets a public DNS name, so if your app talks to your database, that traffic goes over a publicly reachable endpoint with credentials as the boundary. Your .railway.internal hostnames won't resolve, and there's nothing to swap them for except a public address.

Three ways to handle it, roughly in order of preference:

  • Collapse the services. If a service exists only to be called by one other service, consider whether it needs to be separate at all. One app that serves both halves removes the network hop and the credential entirely.
  • Treat the connection as public and secure it accordingly. Long random passwords, TLS where your client supports it, and remove the port binding when nothing external needs it. More on that trade-off.
  • Reconsider whether the extra service earns its place. A cache or queue you added because it was one click away may not be worth a public endpoint — you may not need Redis at all.

If a service cannot be exposed on a public endpoint, this platform may not fit that part of your project.

Variable references become manual wiring

On Railway, a service can reference another's variables, so a database connection string flows into your app without you copying anything. Here, nothing injects a DATABASE_URL — there's no managed database, and no cross-app variable resolution.

That has a practical consequence for the order you do things:

  1. Create the database app first and start it.
  2. Read its address from its port binding.
  3. Set DATABASE_URL on your application with that address and credentials.
  4. Start the application.

The code doesn't change — reading DATABASE_URL from the environment is exactly right. Only the wiring is manual, and it's manual once.

Keep a note of what you set where. Without variable references there's no single source of truth, so the connection string in two places can drift.

Railpack builds need a new deployment path

Railway currently uses Railpack for source builds. Here you can:

  • Write a Dockerfile and push by git. It must be named exactly that and sit at the repository root — not in a subfolder. If you don't have one, your AI tool will write it; check it against these nine points.
  • Upload a ZIP of a supported project and test the detected runtime and start command.

Railpack configuration files are not imported here. Check your build and start commands, system packages and runtime version before choosing ZIP detection or writing a Dockerfile.

Volumes work the same, with one sharper edge

A volume belongs to one application on both platforms, so any design that already worked will keep working.

The difference is what happens when you fill it: if your account's total volume size exceeds its limit, every app in the account is stopped until you free space. That's harsher than a single service running out of room, and it's worth knowing before you migrate something that accumulates data.

What changes in daily work

  • Apps appear in a flat list. Name related apps consistently because there is no project grouping.
  • Each app has its own allocation. CPU, RAM and volume disk are billed per minute. Include every service in a cost comparison, including databases.
  • You can validate a Git build before deployment. Push to build to check whether the image compiles without replacing the running container. See the build-branch guide.

A migration order that works

  1. Inventory your services and mark which ones talk to which. Those edges are the private-networking work.
  2. Choose a build route. Test a supported ZIP project, a Docker image, or a Dockerfile pushed to the build branch.
  3. Deploy stateful services first — database, anything with a volume — and record their addresses.
  4. Set the variables manually, replacing every .railway.internal hostname.
  5. Deploy the app, then check it end to end rather than trusting a green status.
  6. Export and restore data before cutover, then test the app through its platform hostname. Set up your own volume backups before retiring the old service.

FAQ

Does this platform have private networking?

No. Every port binding is publicly addressable, so service-to-service traffic is protected by credentials rather than by network isolation.

What replaces Railway's variable references?

Setting the values yourself. Create the database app first, read its address, then set the connection string on the app that needs it.

Do I need a Dockerfile?

For Git push builds, yes — at the repository root. Alternatively upload a supported project as a ZIP or deploy an image you already built.

Can I run multiple replicas of a service?

No. An app is a single container, which also means no sticky sessions and no cross-instance coordination to configure.

How do scheduled jobs work?

There's no platform cron. Schedule inside your long-running process, and persist the last-run time so a restart doesn't skip or repeat.

Will my volumes work the same way?

Yes, with one caveat: exceeding your account's total disk stops every app until you free space.

Wrapping up

Move one service first and test its build, environment, URL and data. Keep Railway running until the new app passes those checks. If the project depends on private-only connections, variable references, cron or replicas, plan replacements before moving the remaining services.