Docs
Get Started

Railway and Kuberns organize applications in a similar way: a project holds several services, each deployed from a repository, with databases sitting alongside them. The vocabulary translates almost directly.

The part that catches teams out is variables. Railway's reference variables and private hostnames look like ordinary values but are resolved by Railway at deploy time. Copied across as-is, they point at nothing.

What maps directly

On Railway On Kuberns
Project A project
Service deployed from a repository A service with a server resource
Railway environment A Kuberns environment, bound to a branch
Nixpacks build detection The AI agent, which detects framework, runtime, port, and build commands
Custom start command A Procfile command for the web process
Service variables Environment variables, scoped per environment
PostgreSQL, MySQL, MongoDB services Managed datastores
Redis service A Redis queue or cache resource
Public domain A custom domain with automatic SSL
Deploy logs The environment's logs

One difference worth noting: a Railway environment is an arbitrary copy of a project, while a Kuberns environment is tied to a repository branch. If your Railway staging and production environments deploy the same branch, decide which branch each Kuberns environment should track.

What you move yourself

  • Reference variables. Anything written as ${{Postgres.DATABASE_URL}} or pointing at a .railway.internal hostname resolves inside Railway only. Every one of these needs a real Kuberns value. This is Step 4, and it is the step to slow down on.
  • Volumes. The Kuberns documentation does not describe a persistent-volume resource. Anything stored on a Railway volume needs to move to a database or external object storage. Confirm this with the Kuberns team before migrating a service that depends on one.
  • Cron schedules. A Railway cron schedule is service configuration, not something that travels with the repository. Decide whether that work becomes an in-application scheduler inside a worker, or stays external.
  • Your data. Kuberns provisions an empty database. See Step 5.

Before you start

Work service by service. For each one, record:

  • Source repository, branch, and root directory.
  • Build and start commands, if you set them explicitly.
  • Every variable, with its resolved value, not its reference. The Railway dashboard can show the resolved value; that is what you need.
  • Attached databases and volumes, with mount paths.
  • Which services call which other services, and on what hostnames.
  • Public domains and their current TTLs.

Take a database dump using the public connection string Railway provides, not the internal one.

Do not delete the Railway project yet. It is your rollback until Step 8 is verified.

Step 1: Connect the repository

Create the Kuberns service from your Git provider and select the branch Railway deploys. Deploy it as a non-production environment first, on the Kuberns-provided URL. Your live Railway service is unaffected.

The Kuberns onboarding step that chooses a deployment type: Connect Custom Repository beside Use Open source Templates, with Select Your Repository active in the sidebar

If your Railway project has several services, migrate one at a time and start with the one that has the fewest dependencies.

Step 2: Check what the agent detected

The agent reports the framework, root directory, dependency file, base image, web port, build commands, and process commands. Nixpacks and the Kuberns agent are solving the same problem, so their conclusions usually agree.

The Kuberns AI agent analyzing a repository: live detection log on the left, and the Setup, Analyze Repository, Configure Env, Build, and Deploy phase stepper on the right

Where you had overridden Railway's detection with an explicit start command, apply the same override here rather than trusting detection. See agent failures if the result is wrong.

Step 3: Map services and processes

Decide which Railway services become Kuberns services and which become resources on one service:

  • A separate application (an API and a separate frontend) becomes its own Kuberns service in the same project.
  • A worker running the same code as the web process becomes a background worker resource on the same service, with its own Procfile command.
  • A database or Redis service becomes a managed resource, not a service.

Railway represents all of these as services, so this is the one place where the two models genuinely differ.

Step 4: Rebuild your variables

This is the step that determines whether the migration works.

Add your variables as environment variables on the environment, matching key names exactly. Three categories need real thought:

  1. Reference variables. ${{Postgres.DATABASE_URL}}, ${{Redis.REDIS_URL}}, and anything similar must be replaced with the connection values from your new Kuberns resources. Copying the reference text itself will deploy an application that cannot reach its database.
  2. Private hostnames. Any .railway.internal address needs a new value. Confirm how your services will reach each other on Kuberns before you fill these in.
  3. Shared variables. Project-level shared variables do not appear in a service's own variable list. Collect them separately or they will be quietly missing.

The environment's Environment Variables tab listing DATABASE_URL, REDIS_URL, CELERY_BROKER_URL, and CELERY_RESULT_BACKEND, with every value masked

Create the database, cache, and queue resources from the environment's Resources tab.

The environment's Resources tab listing a SERVER, a BACKGROUND WORKER running a celery-start command, a POSTGRES database, and a redis queue, each with its plan, memory, and storage

Connection details for a database, its name, username, password, and hostname, live on its datastore overview page.

Environment variables apply on the next deploy, and saving them triggers one automatically.

Step 5: Move your data

Kuberns gives you an empty managed database and credentials to reach it. Loading the dump is your step.

# PostgreSQL
pg_restore --no-owner --no-acl \
  -h <kuberns-hostname> -U <username> -d <database> \
  railway-dump.sql

Use mysql or mongorestore for the other supported database types.

Kuberns does not restore a backup into a running datastore for you, in either direction. Its own backups are dumps you download and load the same way. Test that path once now, while nothing depends on it.

Run this import twice: once now against the test environment, and once again as the final sync during cutover.

Step 6: Validate before cutting over

Work through the test environment on its Kuberns URL with production-shaped data.

  • The application boots and serves requests.
  • Background workers pick up jobs, and any cron-scheduled work now actually fires.
  • Migrations have run.
  • Every service-to-service call succeeds. If services previously found each other on internal hostnames, this is where you find out whether the replacement values work.
  • Outbound integrations and webhooks reach the new environment.

The environment's Logs tab streaming logs for the web process, each line showing a timestamp, level, source, and message

Step 7: Lower your DNS TTL

Do this a day ahead. Drop the TTL on the records you are about to change to something short, such as 300 seconds, and wait for the old TTL to expire. Your rollback speed on cutover day is set entirely by the TTL you chose the day before.

Step 8: Move the domain

Add the hostname under the environment's Custom Domains, then point DNS at the target Kuberns shows you.

Subdomain   CNAME  api    -> <Kuberns hostname shown in the dashboard>
Root domain A      @      -> <server IP shown in the dashboard>

The Domains tab before a custom domain is added: the default kuberns.site URL marked Active, an empty custom-domain list, and the Add Custom Domain button

The domain moves through DNS verification, SSL provisioning, and activation. See DNS, SSL, and activation if it stalls.

Run the final data sync immediately before the switch, while the Railway service is not accepting writes.

Rollback

Keep the Railway project deployed, with its databases attached, until you are confident. With a short TTL, rolling back is pointing DNS back at Railway.

The rollback stops being clean once the Kuberns environment has taken writes.

Common problems

The application cannot reach its database. A reference variable was copied as text rather than replaced with a real value. Check for ${{ in your environment variables.

A variable is missing that you know you set. It was a project-level shared variable, which does not appear in a service's own list.

One service cannot reach another. An internal hostname was carried over. Those addresses exist only inside Railway.

Workers are idle. Every non-web process needs its own resource on the environment.

Uploaded files disappear after a deploy. The service depended on a Railway volume. This needs external object storage.