Heroku → Fly.io
Move Heroku web and worker processes, configuration, Postgres, Redis, domains, add-ons, releases, schedules, logs, and traffic into Fly.io Machines with explicit container and regional operations.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
Heroku
- Git-based deploys, buildpacks, and add-ons offer an exceptionally approachable platform
- Managed deployments reduce infrastructure and release-management work
- Pricing and infrastructure control become limiting as applications grow
- Runtime assumptions, platform services, and pricing models shape the architecture
Fly.io
- Applications and databases can run close to users with container-level control
- Managed deployments reduce infrastructure and release-management work
- Networking, capacity, and operational debugging require more infrastructure skill
- Runtime assumptions, platform services, and pricing models shape the architecture
Fly.io: Applications and databases can run close to users with container-level control. This removes a major source-side concern: Pricing and infrastructure control become limiting as applications grow.
What you lose: Git-based deploys, buildpacks, and add-ons offer an exceptionally approachable platform. What you inherit: Networking, capacity, and operational debugging require more infrastructure skill.
Know the shape of the move.
This timeline assumes
- Up to 10 apps, 10 process types, and 100 GB of Postgres data
- The application source code and infrastructure dependencies are available for review.
- A production-shaped destination preview runs before traffic changes.
- The source stays intact and billable through the rollback window.
- The migration team records evidence for every blocking verification check.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Buildpacks, Procfile processes, and dynos | partial | critical | Fly can generate a Dockerfile and map processes, but the operating model changes to Machines and fly.toml. | Build a deterministic image and test web, worker, release, health, and shutdown behavior. |
| Postgres and Redis services | partial | critical | Data requires an explicit managed-service choice and copy; operational responsibility differs by product. | Choose managed targets, rehearse restore or replication, and verify backups and monitoring. |
| Heroku add-ons, pipelines, and review apps | manual | high | Fly does not reproduce the Heroku add-on and pipeline marketplace automatically. | Replace each dependency and create separate apps and CI workflows for environments. |
| Secrets and regional networking | manual | critical | Config vars become Fly secrets, while regions, private networking, volumes, and scaling need new decisions. | Import secrets securely and test failover, latency, storage locality, and scale behavior. |
| Source code and static assets | clean | critical | Repository-owned application code can redeploy when the destination supports the framework and runtime. | Pin versions and compare reproducible build output. |
| Build configuration and dependency cache | partial | critical | Build images, commands, paths, caching, and framework detection differ. | Run clean production and preview builds with no source cache. |
| Environment variables and secrets | manual | critical | Secret values and environment scoping do not transfer with code. | Recreate least-privilege values and rotate them after cutover. |
| Functions, workers, and background jobs | partial | critical | Runtime APIs, regions, limits, concurrency, retries, and scheduling differ. | Port and integration-test every endpoint and job. |
| Databases and durable state | partial | critical | State requires a separate dump, restore, replication, or dual-write plan. | Rehearse consistency, downtime, restore time, checksums, and backups. |
| Object, key-value, and persistent storage | partial | critical | Managed stores and attached disks do not move with the application deployment. | Copy and verify every object, key, volume, and access policy. |
| Domains, DNS, and TLS | manual | critical | Custom domains and certificate ownership require destination validation and DNS changes. | Lower TTL, prevalidate domains, and retain exact rollback records. |
| Redirects, rewrites, headers, and routing | partial | critical | Rule syntax, ordering, matching, and proxy behavior differ. | Create a routing registry and test every rule and status code. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| Heroku project, app, or service | Fly.io project or service | manual | Map topology, runtime, region, and ownership. |
| Build command and output | Destination build configuration | transform | Pin runtime and dependency versions. |
| Environment variable or secret | Destination environment variable or secret | manual | Recreate by environment and rotate. |
| Function, worker, or job | Destination compute unit | transform | Port APIs, limits, retries, and schedules. |
| Database or durable store | Destination data service | transform | Migrate and verify separately from code. |
| Domain and certificate | Destination domain and certificate | manual | Prevalidate before DNS changes. |
| Redirect, rewrite, or header | Destination routing rule | transform | Test precedence, conditions, and status. |
| Preview or staging environment | Destination preview or staging environment | manual | Rebuild branch and access behavior. |
| Log, metric, alert, or analytics stream | Destination observability | manual | Reset history after exporting baselines. |
Make the move recoverable.
Create the source-of-truth backup
Preserve Heroku data, configuration, and operating evidence before any destination write.
- Export Heroku build settings, environments, domains, routes, functions, stateful services, access, integrations, and recent releases.
- Create application, data, storage, DNS, certificate, and observability inventories.
- Record traffic, error, latency, build, job, and capacity baselines.
- Preserve the last known-good release and hash all exports.
Proof to capture: A signed manifest reconciles every scoped record class, runtime dependency, export file, count, and hash.
Runtime and routing manifest
Translate application topology, builds, compute, domains, and rules.
- Inventory source values and exceptions.
- Define explicit destination mappings.
- Reject unmapped critical records.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
State and configuration migration
Move data, objects, secrets, and observability deterministically.
- Normalize encoding, dates, identifiers, and blanks.
- Run a representative pilot.
- Reconcile accepted, rejected, and transformed rows.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
The app builds but runtime behavior differs
Routes render while functions, jobs, state, caching, or retries behave differently.
- Consequence
- Users lose data or critical actions.
- Mitigation
- Test complete business paths under production-shaped load.
Stop if: Any critical path lacks durable evidence.
Both platforms write durable state
Source and destination workers, schedules, or web traffic modify the same data.
- Consequence
- State diverges or actions duplicate.
- Mitigation
- Freeze source writers and transfer one owner at a time.
Stop if: An unapproved source writer remains active.
DNS rollback is unavailable
Certificates, proxy settings, or DNS records cannot return traffic quickly.
- Consequence
- An outage outlives the application fix.
- Mitigation
- Prevalidate both directions and preserve exact prior records.
Stop if: Rollback cannot be completed within the approved objective.
Do the work in this order.
- Days 1–3 · inventory
Inventory and decisions
6–12 hours active2–3 days elapsedOwner review waiting- Inventory Heroku data, features, users, domains, and integrations.
- Approve scope, owners, mappings, and exclusions.
Depends on: Heroku and Fly.io administrator access
Stop / go checkpointExport?
Go when: Every critical item has an owner and disposition.
Stop when: Consent, billing, access, or system ownership is unclear.
- Days 3–5 · backup
Backup and reconcile
5–10 hours active1–3 days elapsedExport processing waiting- Create immutable exports and configuration evidence.
- Reconcile counts, totals, and hashes.
Depends on: Approved inventory
Stop / go checkpointTransform?
Go when: Source totals and export manifests agree.
Stop when: Any critical dataset or configuration is absent.
- Days 5–12 · pilot
Map and pilot
10–35 hours active3–8 days elapsedDestination processing and review waiting- Configure Fly.io and transform representative data.
- Run a pilot containing normal records and every critical edge case.
Depends on: Verified backup
Stop / go checkpointScale?
Go when: Pilot mappings, behavior, access, and evidence pass.
Stop when: Any critical check fails or produces unexplained variance.
- Days 10–20 · cutover
Final delta and switch
5–25 hours active1–5 days elapsedDNS, import, or sync propagation waiting- Freeze production writes in Heroku.
- Apply the final delta, switch ownership, and run blocking checks.
Depends on: Passed pilot and approved rollback
Stop / go checkpointOpen production?
Go when: Counts reconcile and one destination system owns production.
Stop when: A source writer remains active or a blocking check fails.
- Days 12–40 · observe
Observe and close
3–18 hours active3–10 days elapsedOperating-cycle evidence waiting- Monitor one complete operating cycle.
- Sign the verification report and close rollback only after stability.
Depends on: Verified cutover
Stop / go checkpointClose rollback?
Go when: No trigger occurs during the agreed observation period.
Stop when: Data, access, delivery, routing, or business results regress.
Cut over with a way back.
Cutover
Make Fly.io the only production system without losing the final Heroku delta.
- Freeze production writes and scheduled actions in Heroku.
- Export, transform, and reconcile the final delta.
- Apply the approved delta to Fly.io.
- Switch domains, forms, integrations, sending, or sync ownership as applicable.
- Run every blocking verification check and keep the source intact.
Proof to capture: Fly.io owns production, totals reconcile, and every blocking check has durable evidence.
Rollback
Return production ownership to Heroku without losing destination-era changes.
- Stop new writes and scheduled actions in Fly.io.
- Restore the prior Heroku routing, forms, integrations, sending, or sync ownership.
- Export the Fly.io post-cutover delta.
- Review and apply safe destination-era changes to Heroku.
- Run the same blocking checks against the restored source.
Proof to capture: Heroku again owns production with current data and no duplicate destination action.
- Unexplained critical count or value variance
- Missing or exposed critical data
- Duplicate production action
- Failed access, routing, delivery, or integration check
- A critical feature has no safe destination replacement
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | Reproducible release | Build a pinned source revision from a clean environment. | Output and dependency versions match acceptance. | Build log and artifact hash. | |
V-02Blocking | HTTP behavior | Crawl routes, redirects, rewrites, headers, errors, and assets. | Approved status, body, and header parity. | Route diff. | |
V-03Blocking | Functions and jobs | Run every endpoint, queue path, schedule, retry, and timeout. | One correct durable result per test. | Runtime log. | |
V-04Blocking | Data and storage reconciliation | Compare counts, checksums, invariants, and backup restore. | Every scoped item is accounted for and recoverable. | State report. | |
V-05Blocking | Secrets and access | Test representative users, services, previews, and production actions. | Least privilege with no secret exposure. | Access evidence. | |
V-06Blocking | DNS, TLS, and rollback | Resolve from multiple networks and exercise rollback records. | Valid TLS and routing within approved objectives. | DNS and certificate report. | |
V-07Blocking | Logs, metrics, traces, and alerts | Generate known failures and load. | Signals arrive with correct context and thresholds. | Observability evidence. | |
V-08Blocking | Single production platform | Inspect traffic, writers, jobs, webhooks, and deployment triggers. | Fly.io alone owns production. | Ownership checklist. |
Remove the scaffolding safely.
Safe after: One complete operating cycle, at least seven stable days, and owner sign-off on every blocking check.
- Create final Heroku exports and archive verification evidence.
- Revoke temporary credentials, API keys, webhooks, and elevated roles.
- Remove obsolete embeds, forms, jobs, integrations, and DNS records.
- Keep the source intact through the approved retention window.
- Cancel paid plans only after billing, legal, and recovery review.
- Schedule the next Fly.io backup, access, and migration-playbook review.
Built to be reviewed.
Tested 2026-07-19. Next scheduled review: 2026-10-19.
- Fly.io: migrate from HerokuAccessed 2026-07-19
- Fly.io: migrate Heroku PostgresAccessed 2026-07-19
- Heroku: export configurationAccessed 2026-07-19