Skip to content

Railway Import

Railway import translates a Railway project into a Forge project and services. It is for moving workloads from Railway onto your own server.

  • A Railway API token. Account, workspace and project tokens all work. Forge only reads from Railway.
  • A connected Forge server that is ready to run services.
  • Permission to create projects in the Forge workspace.
  • For Git services, access to the repository through the workspace’s GitHub connection.

Forge uses the token for the import and never stores it.

Open Imports in the workspace sidebar. The importer walks through four steps:

  1. Connect. Paste your Railway token.
  2. Project. Pick the Railway project and the environment to bring over.
  3. Review. Choose which services to import, the Forge project name, the server to run on, and the options below.
  4. Import. Forge creates everything and shows a live log. Keep the tab open until it finishes.

Forge reads each service’s source (GitHub repository and branch, or Docker image), root directory, build and start commands, variables, and custom domains.

Forge recreates:

  • GitHub services, with their branch, root directory and build and start commands.
  • Docker image services.
  • Databases: PostgreSQL, TimescaleDB, MySQL/MariaDB, Redis, MongoDB, ClickHouse, Qdrant and MinIO, detected from the service image.

Services with no repository or image are shown as skipped.

Leave out RAILWAY_* variables is on by default. Those variables describe Railway’s platform and have no meaning on Forge.

Recreate databases creates Forge database services with fresh credentials.

Start databases first deploys the databases and waits for them to run before deploying apps.

Deploy apps after import queues a deployment for each app. Turn it off to review settings before the first deploy.

Bring custom domains adds each custom domain to the matching Forge service. Railway’s generated *.up.railway.app domains are not copied.

Variables are copied with their current values. Railway references to a recreated Postgres or Redis connection URL, such as ${{Postgres.DATABASE_URL}} or ${{Redis.REDIS_URL}}, become Forge database links. The app then receives the Forge database’s connection URL under the same variable name.

Other references are copied as their current value. The review step flags any value that still points at a Railway host (.railway.internal, .up.railway.app, .proxy.rlwy.net) so you can update it.

The importer recreates your database services on your server. Copying the data inside them is not available yet, so each database is created empty. Your apps are still created and deployed, and the import log says which databases were created empty.

Before you switch traffic, copy the data yourself:

  • PostgreSQL: pg_dump --format=custom from Railway’s public database address, then pg_restore into the Forge database.
  • Redis: redis-cli --rdb from Railway’s public address, then load the dump into the Forge Redis service.

Stop writes to Railway while you copy, then verify the schema, the data and your app before changing DNS. Railway’s .railway.internal addresses only work inside Railway, so turn on public networking (TCP proxy) for the database in Railway to dump it from outside. Never put connection credentials into logs or support messages.

Automatic copying (when enabled by your administrator)

Section titled “Automatic copying (when enabled by your administrator)”

An administrator can switch on automatic copying with FORGE_DATA_MIGRATION_ENABLED=true once the connected server runs a qualified Agent. The import then copies and verifies each database on the server itself, and the apps that use it wait until you approve the switch-over:

  1. Forge checks the source database, copies it into a separate new database, and verifies the copy.
  2. You review the result and approve the switch-over. Any workspace member with database import permission can approve it.
  3. The new database becomes the live one and the previous one is kept for 24 hours.

Automatic copying has size limits: a database up to 20 GiB (256 MiB for ClickHouse), up to 100,000 tables or files, and up to 1,000,000 keys for Redis. Forge checks the source first and stops with a clear message before copying anything; copy anything larger yourself as described above. For Redis, keys that are set to expire within the hour after verification are not compared one by one, because they may legitimately disappear while the check runs; everything else is compared exactly.

Enter the source database version (for example 16.4). Forge reads the real version and accepts any release on the same line, so 16.6 is fine; a different major version (for example 17) needs its own approval.

Before copying, Forge checks free space for the encrypted capture and restored candidate, with 25% headroom. It conservatively requires the combined amount on both storage locations because they may share a disk. PostgreSQL, MySQL, MongoDB and Redis report their size (Redis from the memory its data uses). For a source that cannot (Qdrant, ClickHouse) the check uses the import size limit, which can ask for much more space than the data needs. A fixed, isolated helper can measure storage when the database image has no df utility. Unmeasurable capacity blocks copying. This is a check, not a held reservation; disk exhaustion later still requires cleanup.

For Redis, enter every configured logical database index in order, starting at 0, and explicitly approve the native snapshot in the Railway review form. Forge checks these prerequisites before creating the project. It does not assume that every Redis server has 16 databases or grant snapshot approval automatically.

Automatic retained-database cleanup uses a persisted exponential retry delay, starting at five minutes, with at most eight automatic attempts. After repeated failure, inspect the migration error and use the explicit cleanup action after resolving the cause. Disabling copying hides Retry and Activate; recovery and cancellation remain available.

The source must use an encrypted connection whose certificate Forge can verify.

Railway Postgres. Railway’s Postgres signs its own certificate with a certificate authority unique to that database, and the certificate names localhost rather than the public proxy address. To let Forge verify it fully:

  1. In Railway, open the Postgres service and run this in its Data → Query view (Railway’s own dashboard, not the public connection Forge will use): SELECT pg_read_file('/var/lib/postgresql/data/certs/root.crt');
  2. Paste the result into Source CA certificate in the import form. Leave Name on the source certificate empty and Forge uses localhost for Railway’s proxy addresses.

Forge then connects through the public proxy address but checks the certificate against that database’s own authority and its own name, so a different server cannot impersonate it. If the database presents a self-signed certificate, paste its CA certificate in the import form. Plaintext sources are refused.

Railway Redis and MongoDB. Railway’s public Redis and MongoDB addresses are not encrypted, and Forge never sends a database password over an unencrypted connection. To import one, put an encrypted front in front of it for the duration of the import:

  1. In the same Railway project, add a small service that terminates TLS and forwards to the database over Railway’s private network, for example stunnel accepting TLS on a public TCP port and forwarding to <service>.railway.internal:6379 (Redis) or :27017 (MongoDB).
  2. Give that service a certificate you generate for its public hostname (a self-signed certificate is fine). Keep the private key only in that service.
  3. In the import form use rediss:// for Redis (or the MongoDB address of that front), paste the certificate into Source CA certificate, and set Name on the source certificate to the hostname you generated it for.
  4. Remove the front when the import has been activated and verified.

Forge checks the certificate you pasted, not a public authority, so only your front can answer. Forge has not tested every front; if the connection is refused, the error names the failing step, and the same instructions work for any TLS terminator.

If a copy fails or you cancel before the switch-over, nothing is lost: your apps and ordinary deployments are not blocked, and you can try again with corrected details for the same source server. A pending app deployment that was waiting for the data can be cancelled to release the app.

The exception is an uncertain outcome, for example the server stopping in the middle of the switch-over. Forge cannot tell which database is live, so it holds the affected apps and asks you to run Recover first.

PostgreSQL row-level security is not a supported feature, so Forge does not promise it: in testing on a real PostgreSQL server the dump and restore carried policies across, and the copy stops with an error if a policy names a role that does not exist on the destination or if you rename ownership roles, but you should check your policies yourself after a copy. The import form cannot approve Redis modules, so a Redis server that loads any (Redis 8 loads some built-in ones) is refused; Redis 7 without modules is supported. These advanced features are not supported by automatic copying yet. They are not part of what Forge verifies, so do not rely on them being carried over; check them yourself after a copy: PostgreSQL row-level security; TimescaleDB background jobs and retention policies; MariaDB system-versioned tables; Redis modules; MongoDB views and time-series collections; ClickHouse views, dictionaries, materialized views and projections; and S3 bucket policies, object lock, retention and encryption mapping. After a switch-over Forge keeps the previous database for 24 hours. During that time Switch back to the previous database in the migrations panel makes it live again; anything written to the migrated database since the switch-over is discarded, so Forge asks you to confirm. The migrated copy is then removed. After 24 hours the previous database is removed and switching back is no longer possible. (Application file volumes cannot be switched back.)

Imported domains are added in a pending state. Point each domain’s DNS at your Forge server when you are ready to cut over. Railway keeps serving traffic until DNS changes.

The Imports page lists earlier imports with the Railway project and environment each Forge project came from.

Check the imported project before sending traffic:

  • Confirm all services exist with the expected type.
  • Review variables flagged in the review step and remove Railway-only values.
  • Check the log for databases that were created empty, and copy their data before switching traffic.
  • Deploy any services you did not deploy during the import.
  • Verify custom domains, then switch DNS.
  • Take a manual database backup after the first successful deploy.