How to migrate a production app to a new hosting platform
Why treat migration as risk management
To move a production application to a new hosting platform with minimal downtime and no data loss, treat the move as a risk-management exercise that reduces risk in stages. This guide uses four phases: inventory, parallel run, rollback planning, and cutover. Each phase removes a specific category of risk before you take on the next one.
A production migration takes planning, at least one rehearsal, and a scheduled cutover window. It isn't an afternoon task. The phases apply whether you start on Heroku, Railway, a VPS, or a hyperscaler. For platform-specific steps, use the guides listed under Migration resources and help at the end of this article.
What you need before you start
- An application currently running on Heroku, Railway, a VPS, or a hyperscaler setup.
- Admin access to your source platform's configuration, your data stores, and your DNS provider.
- An account on the target platform where you can provision services and data stores.
- A named decision owner for the cutover, and a maintenance window your stakeholders have agreed to.
The four-phase plan at a glance
Use this checklist to track progress. Each phase has a verification step. Don't start the next phase until that step passes.
- Phase 1: Build the inventory. Verify that every item has an owner.
- Phase 2: Stand up the new stack beside the old one. Verify it with a full, timed data migration dry run.
- Phase 3: Write the rollback plan. Verify that trigger criteria are agreed and the old platform will stay intact.
- Phase 4: Cut over the database and DNS. Verify that exactly one platform accepts writes.
Phase 1: Build a complete pre-migration inventory
The inventory protects you from the most common failure: something that worked on the old platform silently stops on the new one. List everything that must work on the new platform before you retire the old one:
- Services: Web-facing processes, internal or private services, and background workers.
- Crons and scheduled jobs: These often live outside your app config. Check host crontabs and systemd timers on a VPS, or managed schedulers and event rules on a hyperscaler.
- Environment variables and secrets: Watch for values that reference platform resources. Render's Railway migration guide notes that you must replace Railway reference variables such as
${{ Postgres.DATABASE_URL }}with the internal URLs of your new data stores. On a hyperscaler, include secrets your app pulls at runtime from a secrets manager or receives through IAM roles. - Data stores: Relational databases, key-value or cache stores, persistent disks, and object storage.
Also list custom domains and TLS certificates for the DNS cutover.
Give each item an owner who confirms it works on the new platform. Render's guides for migrating from Railway and migrating from Heroku include platform-specific cataloging steps.
Category | What to capture | Common miss |
|---|---|---|
Services | Each process, its start command, and whether it's public, private, or a worker | Internal services with no public URL |
Crons | Schedule, command, and expected runtime | Host crontabs, systemd timers, or jobs in a separate scheduler |
Env vars/secrets | Every key, its source, and whether it points to a platform resource | Auto-injected database URLs, such as Railway reference variables |
Data stores | Engine, version, size, and connection method | Persistent disk mount paths your app expects |
Domains/TLS | Every hostname, its DNS provider, and current TTL | Subdomains used only by webhooks or APIs |
Phase 2: Run both platforms in parallel
In a parallel run, your full stack runs on the new platform while the old one keeps serving production traffic. This is the phase where you find problems cheaply, because production still runs on the old platform.
- Deploy and smoke-test your services on the new platform against non-production data. Note build times and pre-deploy steps now. If builds are slow or complex, see how Render approaches making builds more flexible and performant.
- Rehearse the data migration with the strategy you'll use at cutover (see Phase 4). Restore a copy of production data into the new data store to get real timings for the cutover window.
- Validate behavior: health checks, background jobs, scheduled jobs firing on time, and outbound integrations.
- Size new data stores for production load and lower DNS TTLs.
Do the sizing early. On Render, you can increase Postgres storage only once every 6 hours, and you can't decrease it. Render's migration guides recommend confirming that your Postgres and Key Value instances have enough storage and compute before you move data.
The parallel run is your core risk control. While the old platform stays intact, rollback stays possible. Plan for days or weeks of overlap, not hours.
Decide which platform owns each cron during the overlap. A job running on both can double-process work, such as charging a card twice.
Phase 3: Write the rollback plan before you need it
Define and rehearse rollback before cutover. Your plan needs:
- Trigger criteria: Agreed signals, such as elevated error rates, failed health checks, data mismatches, or failed jobs.
- Decision owner: One person who makes the call during the window.
- Mechanics: Point DNS back to the old platform, which the low TTL makes fast, then restore app config to the old data store.
- Data reconciliation: This is the hardest part, because you must handle every write the new platform accepted after cutover. Exporting new rows misses updates and deletes, so you need a real change record. Use reverse logical replication from the new database to the old one, set up before cutover, or an application-level audit log you can replay. Without either, rollback loses post-cutover writes, and it gets harder the longer the new platform takes writes.
- Decommission window: Keep the old platform intact and paid for until a defined stability period passes.
Test your recovery path too. Render recommends thoroughly testing backup and recovery procedures before you need them. Render Postgres offers point-in-time recovery over the past 7 days on Pro workspaces or higher.
Phase 4: Cut over the database and DNS
Choose a database migration strategy
Your database strategy largely determines how long the cutover window lasts.
Dump/restore takes a logical backup of the source database with pg_dump and restores it into the new one with pg_restore. It's straightforward to rehearse. However, the copy misses any writes made after the dump starts, so you stop writes for the full dump-and-restore duration, which grows with database size. Parallel jobs shorten it: pg_dump runs them for directory-format dumps, and pg_restore restores in parallel from directory or custom format. Render's backup guide uses four parallel jobs for both the dump and the restore, with compression turned off.
Logical replication lets a subscriber database continually pull data changes from a publisher database. You create a publication on the source for the tables you want to replicate, then a subscription on the target that points to it. See the PostgreSQL reference for CREATE PUBLICATION and CREATE SUBSCRIPTION. The subscription copies existing rows, then streams ongoing changes. At cutover, you pause writes only until the target catches up and you repoint your apps.
Plan around the engine's documented restrictions:
- Logical replication requires PostgreSQL 10 or later on both sides.
- It doesn't replicate schema changes (DDL), so create the target schema first and freeze migrations during the overlap.
- It doesn't replicate sequence values, so reset sequences on the target before it accepts writes.
- Tables need a primary key or replica identity to replicate updates and deletes.
Monitor lag with pg_stat_subscription on the target and pg_stat_replication on the source, and cut over only when lag is near zero.
Check what both environments allow. Render Postgres doesn't enable logical replication by default. You request it through Render support. Your workspace must be on a Pro plan or higher, and the database must have at least 10 GB of storage. Render recommends a dedicated read-only role for subscribers, and publishing all tables with CREATE PUBLICATION [name] FOR ALL TABLES requires superuser privileges.
Tell stakeholders plainly: dump/restore always needs a write freeze, and near-zero downtime requires replication plus a rehearsed cutover.
Dimension | Dump/restore | Logical replication |
|---|---|---|
How it works | Logical backup, then restore into the target | Initial sync, then the subscriber continually pulls changes |
Write freeze duration | Full dump and restore time, which grows with size | Only long enough to catch up and repoint apps |
Setup complexity | Lower. Render's guide uses 4 parallel jobs | Higher. You configure publications and subscriptions on both sides |
Best fit | Small databases, tolerable maintenance window | Large or busy databases, strict downtime limits |
Key limitation | Downtime scales with database size | No DDL or sequence replication. On Render, it's off by default and requires a Pro workspace and at least 10 GB storage |
Move DNS with low TTLs
A record's TTL (time to live) tells resolvers how long to cache it. With a high TTL, some users keep reaching the old platform long after you change the record.
- Well before cutover, lower the TTL on affected records and wait at least one full old-TTL period so caches expire. Render's DNS provider guides recommend a 1-minute TTL so Render can verify the domain faster.
- Add your custom domain on the new platform ahead of time so certificate provisioning stays off the critical path.
- At cutover, update the records to point at the new platform.
- Keep the old platform serving during propagation.
- After the cutover is stable, raise the TTL again.
Settle the database cutover first, so only one side accepts writes to your primary data store. During propagation, keep the old platform from writing to the old database. You have three options: repoint its apps at the new primary if networking allows, put it in read-only or maintenance mode, or redirect requests to the new platform's default hostname. Pick one during the rehearsal.
Certificate issuance can lag behind DNS changes, which is why step 2 adds the domain early. Render issues and renews TLS certificates automatically through Let's Encrypt and Google Trust Services, so you don't need to bring your own.
Operational guidance and pitfalls
Do
- Run at least one full, timed dry run with production-sized data.
- Schedule the cutover for off hours. Render's migration guides note that moving data and DNS requires downtime, even if brief.
- Prefer a maintenance window when you can tolerate one. Pausing traffic removes most of the moving parts from the cutover.
- If you rotate secrets during the move, overlap old and new credentials so no running service holds a revoked one.
Don't
- Don't let both platforms accept writes to the primary database.
- Don't run the same cron on both platforms at once.
- Don't copy auto-injected connection strings into the new platform.
Common mistakes
- Missed crons: A nightly report or cleanup silently stops after cutover.
- Stale DNS caches: Old traffic persists for hours because you lowered the TTL too late.
- Stale sequences: Inserts fail with duplicate-key errors after a replication cutover.
- Early decommissioning: You discover a bug after deleting the old stack, so rollback is no longer possible.
After the cutover
Once the new platform is stable, raise your TTLs, remove temporary replication, and retire the old platform on schedule. Then write a short retrospective to improve your inventory template for the next migration.
Migration resources and help
Render's team helps orchestrate migrations to minimize downtime, including apps with multi-terabyte databases. For a complex move, contact the team through the migration credits page, which offers credits and a migration review for teams moving to Render. Confirm the current terms before you build credits into your budget.
Check your source platform's documentation too. Most platforms document how to export environment variables and take a pg_dump of their managed Postgres, and those steps feed directly into your inventory and dry run.
For step-by-step walkthroughs, use Render's dedicated articles:
- Migrate from Railway to Render covers service mapping and data moves.
- If you're coming from Replit, use Render's Replit migration article for the step-by-step path.
- Migrate from Heroku to Render covers Heroku-specific mapping. For an overview of the tooling that simplifies Heroku moves, see It's never been easier to move from Heroku to Render.
- For database details, see Render's docs on Postgres logical replication and Postgres backups.