← All playbooks
Hosting migration

Render → Fly.io

Move a Render web service onto Fly.io machines, and move Render Postgres with pg_dump into Fly Managed Postgres. Disks and env vars do not come along with the image.

Typical timeline3 to 10 days12 to 30 hours active work
Statusneeds review
Source profileRender Postgres recovery docs reviewed 2026-11-03
Destination profileFly Managed Postgres import and app docs reviewed 2026-11-03
Documentation date
Review window
Evidence4 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

Render

Reasons to stay
  • Straightforward web services, workers, databases, and static sites cover common architectures
  • Managed deployments reduce infrastructure and release-management work
Reasons to leave
  • Regional options, platform depth, and scaling controls are less extensive than larger clouds
  • Runtime assumptions, platform services, and pricing models shape the architecture
New platform

Fly.io

What gets better
  • Applications and databases can run close to users with container-level control
  • Managed deployments reduce infrastructure and release-management work
What gets worse
  • Networking, capacity, and operational debugging require more infrastructure skill
  • Runtime assumptions, platform services, and pricing models shape the architecture
Best of the move

Fly.io: Applications and databases can run close to users with container-level control. This removes a major source-side concern: Regional options, platform depth, and scaling controls are less extensive than larger clouds.

Worst of the move

What you lose: Straightforward web services, workers, databases, and static sites cover common architectures. What you inherit: Networking, capacity, and operational debugging require more infrastructure skill.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome11 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 4, Manual rebuild: 5, Not transferred: 2.
Clean0
Partial4
Manual5
Lost2
Mapping route9 of 9 fields have a destination path

This timeline assumes

  • A small set of Render web services, one Render Postgres database, and optional persistent disks.
  • The database is under Fly Managed Postgres limits: 1 TB max, and 500 GB at first create unless you expand after.
  • You can reach Render's external database URL and run fly mpg proxy from a laptop.
  • The site already documents Render → Railway and Render → Heroku if you want a comparison.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Web servicepartialcriticalYou deploy the app again on Fly.io. Render's service settings do not become a fly.toml by themselves.Write the image, port, region, and health check. Deploy to a staging app before DNS.
Environment variablesmanualcriticalSecrets stay in Render until you set them on Fly.io. A git push does not copy them.Set Fly secrets from a password manager, not from a screenshot in Slack. Rotate anything that was shared.
Postgres datapartialcriticalFly's Managed Postgres import guide says there is no automated migration. The simple path is pg_dump of the source piped into psql through fly mpg proxy on localhost port 16380. The new cluster's volume must be at least as large as the database or the import fails.Create the cluster with enough disk. Restore into an empty cluster and compare counts.
Extensions and sizepartialcriticalFly says an import fails when the source needs an extension Managed Postgres does not offer. Databases over 1 TB do not fit. Creation starts at 500 GB or less. You expand afterward for anything bigger. pgvector and PostGIS are the third-party extensions Fly lists as supported.List extensions on Render before you create the cluster.
Render backupsmanualhighPaid Render Postgres has point-in-time recovery: 3 days on Hobby, 7 days on Pro or higher. Recovery creates a new instance. You cannot restore to a time within ten minutes of now. Free compute has no recovery. Logical exports are .dir.tar.gz files kept for seven days. pg_dump from the external URL is the copy you can move.Take a pg_dump you store outside Render before cutover. Do not rely on the seven-day export alone.
Persistent disksmanualcriticalA Render disk is not in the Git repo. Uploads and other files need their own copy onto a Fly volume.Inventory mount paths. Copy files and open one of each type after deploy.
RegionsmanualmediumFly runs the app in the region you set. Render's region does not move with the service.Pick the region closest to your users and measure latency before DNS.
Custom domains and TLSmanualcriticalCertificates are issued on Fly.io after you add the hostname. Render's certificate does not transfer.Add the domain, lower TTL, and keep the old records written down.
Background jobs and cronpartialhighRender cron jobs and workers are separate services. Fly needs a process or a scheduled machine for each one.List every schedule. Run each once on staging before you disable Render.
Unmanaged Fly PostgreslosthighFly's older unmanaged Postgres getting-started page is archived. Fly says support will not guide you on it, and points new databases at Managed Postgres.Create a Managed Postgres cluster. Treat 2024 writeups that use unmanaged Postgres as history, not as the current path.
Logs and metrics historylostmediumRender's log history stays in Render. Fly starts collecting after the new app runs.Export any log evidence you still need before you suspend Render.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
Render web serviceFly appmanualSet the internal port, region, and VM size in fly.toml or the dashboard.
Render env varFly secretmanualRecreate each one. Rotate shared values.
Render Postgres external URLpg_dump sourcetransformDump with --no-owner. Render's example uses -n public.
pg_dump streamManaged Postgres via fly mpg proxytransformProxy localhost:16380. Use the fly-user connection string with that host.
Render diskFly volumemanualCopy files. A deploy does not include the disk.
Render cronFly scheduled machine or processmanualRun it once on staging.
Render custom domainFly certificatemanualAdd the hostname before you change DNS.
Render regionFly primary regionmanualChoose it on purpose. It does not copy.
Postgres extensionManaged Postgres extensionmanualStop if you need an extension Fly does not list.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep a Render dump you can restore, and keep Render running until Fly answers for the domain.

  1. Write down services, regions, disk paths, cron, and domains.
  2. Run pg_dump against the external database URL and store it off Render.
  3. Copy disk contents to a tarball.
  4. Export any log evidence you need.
  5. Leave Render's paid database in place so point-in-time recovery still exists during the window.

Proof to capture: The dump restores to a scratch database, and the tarball lists the disk paths you care about.

Transformation · pg_dump, fly mpg proxy, and psql

Database import

Load Render Postgres into a new Fly Managed Postgres cluster.

  1. Create the cluster with a volume at least as large as the source. Stay under the 500 GB initial cap, then expand if you must.
  2. Run fly mpg proxy and point the connection string at localhost:16380.
  3. pg_dump the Render external URL and pipe it into psql, or split the job if the database is large. Fly also mentions pgloader and pgcopydb for harder cases.
  4. Compare table counts before you change the app's DATABASE_URL.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · fly launch and a volume mount

App and disk

Boot the service on Fly and put uploaded files on a volume.

  1. Deploy a staging app with the same start command and port.
  2. Set secrets.
  3. Copy the disk tarball onto the volume and open a sample file.
  4. Point the staging app at Managed Postgres and run one write.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Pilot

The volume is smaller than the dump

criticalpossible likelihood

Fly says the import fails when the new volume is smaller than the database.

Consequence
You get a partial cluster and a false sense that the dump is loaded.
Mitigation
Size the volume first. Check counts after psql exits.

Stop if: Table counts do not match the dump.

Cutover

Both hosts write the database

criticalpossible likelihood

Render still has the old DATABASE_URL while Fly is live.

Consequence
Rows split across two databases.
Mitigation
One writer. Suspend Render services before the final dump.

Stop if: Render logs show writes after the dump started.

Cutover

Disk files never left Render

highlikely likelihood

The app boots and the upload directory is empty.

Consequence
Users see broken images or missing documents.
Mitigation
Copy the disk and request a known file.

Stop if: A file that exists on Render 404s on Fly.

06Precise timeline

Do the work in this order.

Estimate forOne web service, one Postgres database under a few hundred GB, and one disk of uploads
Total elapsed3 to 10 days
Active work12 to 30 hours
BufferAdd time for extensions Fly does not ship, databases near 500 GB, or a disk so large that copying it dominates the window.
01
Days 1 to 2List services and extensions1 to 2 days
02
Days 2 to 3Dump and tarball1 day
03
Days 3 to 7Staging app2 to 4 days
04
Days 7 to 8Final dump and DNS1 day
05
Days 8 to 15Keep Render recoverable7 days
  1. Days 1 to 2 · inventory

    List services and extensions

    3 to 6 hours active1 to 2 days elapsedOwner confirms region waiting
    • List services, cron, disks, and Postgres extensions.
    • Check database size against Fly's 500 GB create cap and 1 TB max.

    Depends on: Render admin and a Fly org

    Stop / go checkpoint

    Create the cluster?

    Go when: Every extension is supported or dropped on purpose.

    Stop when: You need an unsupported extension or more than 1 TB.

  2. Days 2 to 3 · backup

    Dump and tarball

    2 to 6 hours active1 day elapsedpg_dump waiting
    • pg_dump to storage you control.
    • Tarball the disk.

    Depends on: External database URL

    Stop / go checkpoint

    Restore on Fly?

    Go when: The dump loads in a scratch database.

    Stop when: pg_dump errors.

  3. Days 3 to 7 · pilot

    Staging app

    6 to 14 hours active2 to 4 days elapsedImport and certificate waiting
    • fly mpg create and import through the proxy.
    • Deploy the app, copy the disk, and hit a staging URL.

    Depends on: Dump

    Stop / go checkpoint

    Cut DNS?

    Go when: Counts match and a write succeeds on staging.

    Stop when: The import fails the size or extension check.

  4. Days 7 to 8 · cutover

    Final dump and DNS

    2 to 6 hours active1 day elapsedDNS waiting
    • Suspend Render writers.
    • Take a final dump and import it.
    • Move DNS.

    Depends on: Staging passed

    Stop / go checkpoint

    Fly owns traffic?

    Go when: The domain serves Fly and Render is idle.

    Stop when: Render still writes.

  5. Days 8 to 15 · observe

    Keep Render recoverable

    2 to 4 hours active7 days elapsedReal traffic waiting
    • Watch errors and disk space.
    • Keep the Render database until the window ends.

    Depends on: DNS

    Stop / go checkpoint

    Suspend Render for good?

    Go when: A week of traffic looks like the Render baseline.

    Stop when: You need Render point-in-time recovery.

07The point of change

Cut over with a way back.

Go live

Cutover

Fly.io serves the app and holds the only new database writes.

Recommended window: A low-traffic hour with the database dump already rehearsed.

  1. Suspend Render workers and the web service.
  2. pg_dump Render and import through fly mpg proxy.
  3. Copy any disk delta.
  4. Point DNS at Fly and confirm TLS.
  5. Watch Render logs for a stray writer.

Proof to capture: Counts match, the domain is Fly, and Render shows no new queries.

Return to safety

Rollback

Send traffic back to Render, which still has its data from before the final dump.

Deadline: Before you delete the Render database. Logical exports there only last seven days, and free instances have no recovery.

  1. Stop the Fly app from taking writes.
  2. Restore the previous DNS records.
  3. If Fly accepted writes, dump those rows before you resume Render.
  4. Resume the Render service against its original database.
  5. Place a test request.

Proof to capture: Render serves the domain and its database is the one you did not drop.

Rollback immediately when
  • Table counts differ
  • An extension is missing
  • Uploads 404
  • Render still writes after cutover
  • TLS or the health check fails
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 8 checks passed
PassIDCheckMethodExpected resultEvidence
V-01BlockingExtensionsCompare pg_extension on Render with Fly's supported list.Every required extension exists on Managed Postgres.Extension list.
V-02BlockingTable countsCount rows on both sides after the pilot import.Counts match for every table in scope.Count sheet.
V-03BlockingStaging bootRequest the health URL on the Fly staging app.The process listens on the port in fly.toml and reads Managed Postgres.HTTP check.
V-04BlockingKnown fileOpen a file that lived on the Render disk.The file is on the Fly volume.File request.
V-05BlockingApp configBoot with Fly secrets and without a copied .env in the image.The app reads secrets and the image has no secret file.Boot log.
V-06BlockingOne cron runTrigger each schedule on staging.One successful run and no Render twin.Job log.
V-07BlockingTLSResolve the hostname and open it.Fly's certificate is valid.Certificate.
V-08BlockingRender idleRead Render logs after suspend.No new queries or deploys.Render log.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: Seven days of Fly traffic, with the offsite pg_dump still in storage.

  1. Keep the pg_dump for the retention period you agreed.
  2. Revoke the Render external database credential you used for the dump.
  3. Suspend Render services only after rollback is closed.
  4. Delete the Render database last. Its logical exports expire in seven days.
  5. Remove staging Fly apps you no longer need.
  6. Set a Fly backup you can restore with fly mpg restore, and test it once.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. Render: Postgres recovery and backupsAccessed 2026-11-03
  2. Fly.io: import into Managed PostgresAccessed 2026-11-03
  3. Fly.io: appsAccessed 2026-11-03
  4. Fly.io: Managed PostgresAccessed 2026-11-03