Skip to content
Discord Get Started

Migrate from Railway

Railway provides managed PostgreSQL databases as part of its application hosting platform. Since Railway runs standard PostgreSQL, migration to DB9 uses the same pg_dump / import workflow as any PostgreSQL migration.

For the general PostgreSQL migration guide, see Migrate from PostgreSQL.

  • SQL compatibility — DB9 supports the same DML, DDL, joins, CTEs, window functions, and subqueries you use in Railway PostgreSQL. Most queries work without changes.
  • PostgreSQL drivers — Any driver that connects via pgwire (node-postgres, psycopg, pgx, JDBC) works with DB9.
  • ORM compatibility — Prisma, Drizzle, SQLAlchemy, TypeORM, Sequelize, Knex, and GORM are tested and supported.
  • Data types — Common types (TEXT, INTEGER, BIGINT, BOOLEAN, TIMESTAMPTZ, UUID, JSONB, arrays, vectors) work identically.
AreaRailway PostgreSQLDB9
Connection stringpostgresql://postgres:pass@host.railway.app:port/railwaypostgresql://tenant.role@pg.db9.io:5433/postgres
PortVaries per deployment5433
Database namerailway (default)Always postgres
Connection poolingNo built-in poolerNo built-in pooler — use application-side pooling
ExtensionsMost community extensions available9 built-in (http, vector, fs9, pg_cron, embedding, hstore, uuid-ossp, parquet, zhparser)
ReplicationLogical replication supportedNot supported
Table partitioningSupportedNot supported
LISTEN/NOTIFYSupportedNot supported
Deployment couplingTight integration with Railway services via $DATABASE_URLStandalone — connect from any platform

Review the Compatibility Matrix for the full list of supported and unsupported features.

  • Access to your Railway project and database credentials
  • Railway CLI installed (npm install -g @railway/cli) or connection string from the Railway dashboard
  • pg_dump installed locally (comes with PostgreSQL client tools)
  • DB9 CLI installed: curl -fsSL https://db9.ai/install | sh
  • A DB9 account: db9 create --name my-app to create your target database
  1. Get Your Railway Connection String

    Option A: Railway dashboard

    Go to your project → PostgreSQL service → Variables tab → copy DATABASE_URL.

    Option B: Railway CLI

    Terminal
    railway login
    railway link # Link to your project
    railway variables get DATABASE_URL

    The connection string looks like:

    postgresql://postgres:password@host.railway.app:12345/railway
  2. Export from Railway

    Use pg_dump with the Railway connection string.

    Schema and data (plain SQL format)

    Terminal
    pg_dump --no-owner --no-privileges --no-comments \
    "postgresql://postgres:password@host.railway.app:12345/railway" \
    > export.sql

    Schema only

    Terminal
    pg_dump --schema-only --no-owner --no-privileges \
    "postgresql://postgres:password@host.railway.app:12345/railway" \
    > schema.sql

    Flags explained:

    • --no-owner — omits ALTER ... OWNER TO statements that reference Railway-specific roles
    • --no-privileges — omits GRANT/REVOKE statements
    • --no-comments — omits COMMENT ON statements

    Use plain SQL format (default). DB9 does not support pg_restore with the custom (-Fc) or directory (-Fd) formats — import via SQL text only.

  3. Clean the Export

    The pg_dump output may contain statements that DB9 does not support. Remove or comment out:

    • CREATE EXTENSION for extensions DB9 does not have — DB9 supports 9 built-in extensions. Remove any CREATE EXTENSION for extensions not in: http, uuid-ossp, hstore, fs9, pg_cron, parquet, zhparser, vector, embedding.
    • CREATE PUBLICATION / CREATE SUBSCRIPTION — DB9 does not support logical replication.
    • Row-level security policiesCREATE POLICY, ALTER TABLE ... ENABLE ROW LEVEL SECURITY.
    • Table partitioningPARTITION BY, CREATE TABLE ... PARTITION OF.

    A quick way to identify issues:

    Terminal
    # Check for unsupported extensions
    grep "CREATE EXTENSION" export.sql
    # Check for partitioning
    grep -i "PARTITION" export.sql
    # Check for RLS
    grep -i "ROW LEVEL SECURITY\|CREATE POLICY" export.sql
    # Check for replication
    grep -i "PUBLICATION\|SUBSCRIPTION" export.sql
  4. Create the DB9 Database

    Terminal
    db9 create --name my-app --show-connection-string

    This returns immediately with the connection string and credentials. Save them for your application config.

  5. Import into DB9

    Option A: CLI import (recommended for most databases)

    Terminal
    db9 db sql my-app -f export.sql

    Suitable for databases up to the API import limits (50,000 rows or 16 MB per table).

    Option B: Direct psql import (for larger databases)

    Terminal
    psql "$(db9 db status my-app --json | jq -r .connection_string)" -f export.sql

    Streams SQL through pgwire without API size limits.

    Option C: COPY for bulk data

    Terminal
    # Import schema first
    psql "$(db9 db status my-app --json | jq -r .connection_string)" -f schema.sql
    # Then stream data directly from Railway into DB9
    pg_dump --data-only --no-owner \
    "postgresql://postgres:password@host.railway.app:12345/railway" \
    | psql "$(db9 db status my-app --json | jq -r .connection_string)"

    DB9 supports COPY in CSV and TEXT formats over pgwire.

  6. Update Your Application

    Connection string

    Replace the Railway connection string with DB9’s:

    Diff
    DATABASE_URL=postgresql://postgres:password@host.railway.app:12345/railway
    DATABASE_URL=postgresql://a1b2c3d4e5f6.admin@pg.db9.io:5433/postgres?sslmode=require

    Key differences:

    • Username: DB9 uses {tenant_id}.{role} format (e.g., a1b2c3d4e5f6.admin)
    • Port: 5433
    • Database: Always postgres
    • Host: pg.db9.io

    Railway service variables

    If your Railway services reference $DATABASE_URL as a shared variable, update the variable in each service that connects to the database, or set a new variable pointing to DB9 and update your code to use it.

    Connection pooling

    Railway does not include a built-in connection pooler, so your application likely already handles pooling. Verify your pool settings work with DB9:

    TypeScript
    const pool = new pg.Pool({
    connectionString: process.env.DATABASE_URL,
    max: 10,
    idleTimeoutMillis: 30000,
    });

    For ORMs, see the integration guides: Prisma, Drizzle, SQLAlchemy.

  7. Validate

    Check schema

    Terminal
    db9 db dump my-app --ddl-only

    Compare the output with your original schema to confirm all tables, indexes, and constraints were created.

    Check row counts

    Terminal
    db9 db sql my-app -q "SELECT count(*) FROM users"
    db9 db sql my-app -q "SELECT count(*) FROM orders"

    Compare row counts against the source Railway database.

    Run your test suite

    Terminal
    DATABASE_URL="$(db9 db status my-app --json | jq -r .connection_string)" npm test

    Check for unsupported features

    If your tests fail, check these common differences:

    • SERIALIZABLE isolation — DB9 does not support SERIALIZABLE and returns an error. Use REPEATABLE READ or READ COMMITTED instead
    • LISTEN/NOTIFY — not supported; use polling or an external message queue
    • Advisory locks — available, but coordination is node-local. For strict row-level coordination, use SELECT ... FOR UPDATE

If you need to revert:

  1. Your Railway database is unchanged — switch DATABASE_URL back to the Railway connection string.
  2. If you need to export data created in DB9 back to Railway:
Terminal
# Export from DB9
db9 db dump my-app -o db9-export.sql
# Import to Railway
psql "postgresql://postgres:password@host.railway.app:12345/railway" \
-f db9-export.sql

The db9 db dump command outputs plain SQL (up to 50,000 rows or 16 MB per table). For larger databases, use psql to stream individual tables with COPY.

  • No zero-downtime migration — DB9 does not support logical replication, so you cannot stream changes from Railway in real time. Plan a maintenance window or accept a brief cutover period.
  • Extension gaps — If your Railway database uses extensions not in DB9’s built-in set (e.g., PostGIS, pg_trgm, pgcrypto), those features will not be available. Check your CREATE EXTENSION statements.
  • Dump size limits — The db9 db sql -f API import has limits (50,000 rows, 16 MB per table). For larger databases, use direct psql connection for import.
  • Railway integration loss — Railway’s tight coupling between services (automatic DATABASE_URL injection, private networking) will not apply to DB9. Update each service’s environment variables manually.