From 54700bb0aee8eabb58ceaa4975155760b820a086 Mon Sep 17 00:00:00 2001 From: Sprite Date: Wed, 11 Feb 2026 12:00:54 +0000 Subject: [PATCH 1/3] Move Heroku migration guide to top-level getting-started section - Replace Rails-specific migration doc with a language-agnostic guide covering Node.js, Python, Ruby, Go, and more - Add new doc at /docs/getting-started/migrate-from-heroku/ - Remove old doc from /docs/rails/getting-started/migrate-from-heroku/ with redirect_from for backwards compatibility - Add sidebar nav entry under Getting Started Co-Authored-By: Claude Opus 4.6 --- getting-started/migrate-from-heroku.html.md | 334 +++++++++++++++++ partials/_firecracker_nav.html.erb | 1 + .../migrate-from-heroku.html.md | 338 ------------------ 3 files changed, 335 insertions(+), 338 deletions(-) create mode 100644 getting-started/migrate-from-heroku.html.md delete mode 100644 rails/getting-started/migrate-from-heroku.html.md diff --git a/getting-started/migrate-from-heroku.html.md b/getting-started/migrate-from-heroku.html.md new file mode 100644 index 0000000000..c60c2d7d63 --- /dev/null +++ b/getting-started/migrate-from-heroku.html.md @@ -0,0 +1,334 @@ +--- +title: Migrate from Heroku +layout: docs +nav: firecracker +toc: true +redirect_from: /docs/rails/getting-started/migrate-from-heroku/ +--- + +This guide will walk you through migrating your Heroku app to Fly.io. Just follow the steps in order. + +**What this guide covers:** +- Web apps in any language (Node.js, Python, Ruby, Go, etc.) +- Postgres databases +- Redis +- Background workers +- Environment variables +- Custom domains + +**Time required:** Most apps can be migrated in under 30 minutes. + +## TL;DR - The Fast Path + +For experienced developers, here's the entire migration in 7 commands: + +```bash +# 1. Export secrets (excluding Heroku-managed vars) +heroku config -s -a YOUR_HEROKU_APP | grep -v '^DATABASE_URL\|^REDIS_URL\|^HEROKU_' > secrets.txt + +# 2. Create Fly app (generates Dockerfile automatically) +cd your-app-directory +fly launch --no-deploy + +# 3. Create Managed Postgres and import data +fly mpg create --name your-db --region iad +# Note the CLUSTER_ID from output, then: +fly mpg proxy CLUSTER_ID --local-port 15432 & +pg_dump --no-owner --no-acl "$(heroku config:get DATABASE_URL -a YOUR_HEROKU_APP)" | \ + PGPASSWORD=YOUR_FLY_DB_PASSWORD psql -h localhost -p 15432 -U fly-user -d fly-db +fly mpg attach CLUSTER_ID -a your-fly-app + +# 4. Import secrets +cat secrets.txt | fly secrets import + +# 5. Deploy +fly deploy + +# 6. Verify +curl https://your-fly-app.fly.dev +``` + +For a more detailed explanation, read on. + +## Prerequisites + +Install both CLIs: + +```bash +# Install Fly CLI +curl -fsSL https://fly.io/install.sh | sh + +# Sign up / log in to Fly +fly auth signup # or: fly auth login + +# Make sure you're logged into Heroku +heroku login +``` + +## Step 1: Export Your Heroku Configuration + +First, let's capture everything about your Heroku app. + +```bash +# Set your Heroku app name +export HEROKU_APP="your-app-name" + +# Export environment variables (excluding Heroku-managed ones) +heroku config -s -a $HEROKU_APP | grep -v '^HEROKU_' | grep -v '^DATABASE_URL' | grep -v '^REDIS_URL' > heroku-env.txt + +# See what add-ons you have +heroku addons -a $HEROKU_APP + +# Check your current process types +heroku ps -a $HEROKU_APP +``` + +Take note of: +- Your add-ons (Postgres, Redis, etc.) +- Your dyno types (web, worker, etc.) + +## Step 2: Prepare Your App + +Make sure you have your app's source code locally: + +```bash +# If you don't have it locally, clone from Heroku +heroku git:clone -a $HEROKU_APP +cd $HEROKU_APP +``` + +### Check for a Dockerfile + +Fly works best with a Dockerfile. If you don't have one, that's fine - `fly launch` will generate one for you based on your app type. + +### Check your Procfile + +Your Heroku `Procfile` maps directly to Fly processes. A typical Procfile: + +```procfile +web: npm start +worker: npm run worker +release: npm run db:migrate +``` + +## Step 3: Launch on Fly + +From your app directory: + +```bash +fly launch +``` + +This will: +1. Detect your app type (Node, Python, Rails, etc.) +2. Generate a Dockerfile if needed +3. Create a `fly.toml` configuration file +4. Ask if you want to provision a Postgres database + +**When prompted:** +- Choose a name for your app (or accept the generated one) +- Select your preferred region +- Say **Yes** to Postgres if you need a database (choose "Managed Postgres" for the fully-managed option) +- Say **No** to deploy now - we'll do that after setting up secrets + +## Step 4: Set Up Your Database + +If you have Heroku Postgres, create a Managed Postgres cluster on Fly: + +```bash +fly mpg create --name your-app-db --region iad +``` + +Save the connection details it outputs! You'll see something like: +``` +Connection string: postgresql://fly-user:PASSWORD@pgbouncer.CLUSTER_ID.flympg.net/fly-db +``` + +To import your data, use `fly mpg proxy` to create a local tunnel, then `pg_dump` and `psql`: + +```bash +# Terminal 1: Start a proxy to your Fly database (use the cluster ID from the output above) +fly mpg proxy CLUSTER_ID --local-port 15432 + +# Terminal 2: Dump from Heroku and restore to Fly +export HEROKU_DB=$(heroku config:get DATABASE_URL -a $HEROKU_APP) + +# Dump the database (plain SQL format) +pg_dump --no-owner --no-acl "$HEROKU_DB" > heroku-dump.sql + +# Restore to Fly (via the proxy) +PGPASSWORD=YOUR_PASSWORD psql -h localhost -p 15432 -U fly-user -d fly-db -f heroku-dump.sql +``` + +You'll see some errors about Heroku-specific extensions (pg_stat_statements, event triggers) - these are safe to ignore. Your actual data will import successfully. + +Attach the database to your app: + +```bash +fly mpg attach CLUSTER_ID -a your-app-name +``` + +This automatically sets `DATABASE_URL` as a secret on your app. + +## Step 5: Set Up Redis (if needed) + +If you use Heroku Redis or Redis Cloud: + +```bash +fly redis create +``` + +Follow the prompts. This uses Upstash Redis which is fully managed. + +After creation, attach it to your app: + +```bash +# The create command outputs a REDIS_URL - set it as a secret +fly secrets set REDIS_URL="redis://default:xxxxx@fly-your-redis.upstash.io:6379" -a your-app-name +``` + +**Note:** Fly Redis doesn't support importing existing data. For most use cases (caching, sessions), this is fine - the cache will warm up naturally. + +## Step 6: Import Environment Variables + +Remember that `heroku-env.txt` file we created? Let's import it: + +```bash +# Import all your Heroku environment variables +cat heroku-env.txt | fly secrets import -a your-app-name +``` + +Double-check that secrets were imported: + +```bash +fly secrets list -a your-app-name +``` + +## Step 7: Configure Processes (Web + Workers) + +If your app has multiple process types (web + worker), edit your `fly.toml`: + +```toml +[processes] + web = "npm start" + worker = "npm run worker" + +# The web process gets the HTTP service +[[services]] + internal_port = 8080 + processes = ["web"] + # ... rest of service config +``` + +For a release command (like database migrations), add: + +```toml +[deploy] + release_command = "npm run db:migrate" +``` + +## Step 8: Deploy + +```bash +fly deploy +``` + +Watch the deployment. If something fails, check the logs: + +```bash +fly logs +``` + +## Step 9: Verify Your App + +```bash +# Open your app in a browser +fly open + +# Check the status +fly status + +# View live logs +fly logs +``` + +## Step 10: Migrate Your Domain + +If you have a custom domain on Heroku: + +```bash +# Add your domain to Fly +fly certs add yourdomain.com -a your-app-name + +# Get the IP addresses to point your DNS to +fly ips list -a your-app-name +``` + +Update your DNS: +- For apex domains (example.com): Create an A record pointing to the IPv4 address +- For subdomains (www.example.com): Create a CNAME pointing to `your-app-name.fly.dev` + +Once DNS propagates, Fly automatically provisions an SSL certificate. + +## Quick Reference: Command Mapping + +| Heroku | Fly | +|--------|-----| +| `heroku apps` | `fly apps list` | +| `heroku logs -t` | `fly logs` | +| `heroku ps` | `fly status` | +| `heroku config` | `fly secrets list` | +| `heroku config:set KEY=value` | `fly secrets set KEY=value` | +| `heroku run bash` | `fly ssh console` | +| `heroku run rails console` | `fly ssh console -C "rails console"` | +| `heroku pg:psql` | `fly mpg connect CLUSTER_ID` | +| `heroku restart` | `fly apps restart` | +| `heroku scale web=2` | `fly scale count web=2` | +| `heroku maintenance:on` | See [maintenance mode docs](/docs/apps/maintenance/) | + +## Common Add-on Replacements + +| Heroku Add-on | Fly Equivalent | +|---------------|----------------| +| Heroku Postgres | `fly mpg create` (Managed Postgres) | +| Heroku Redis / Redis Cloud | `fly redis create` (Upstash) | +| Heroku Scheduler | Use cron in your app, or a separate "cron" process | +| Papertrail | `fly logs` built-in, or add a log drain | +| SendGrid | Keep using SendGrid (just use API key) | +| New Relic | `fly extensions sentry` or keep using New Relic | +| S3 / Bucketeer | `fly storage create` (Tigris) or keep using S3 | +| Elasticsearch | Deploy Elasticsearch as a Fly app, or use a hosted service | +| Memcached | Use Redis instead (`fly redis create`) | + +## Multiple Environments (Staging/Production) + +Fly doesn't have "pipelines" like Heroku. Instead, create separate apps: + +```bash +# Production +fly launch --name myapp + +# Staging +fly launch --name myapp-staging +``` + +For CI/CD, use GitHub Actions with the [Fly GitHub Action](/docs/launch/continuous-deployment-with-github-actions/). + + +## Shutting Down Heroku + +Once everything is working on Fly: + +1. Update your DNS to point to Fly +2. Wait for DNS propagation (check with `dig yourdomain.com`) +3. Monitor both apps for a day to ensure no traffic goes to Heroku +4. Scale down Heroku dynos: `heroku ps:scale web=0 worker=0 -a $HEROKU_APP` +5. Cancel Heroku add-ons +6. Delete the Heroku app: `heroku apps:destroy -a $HEROKU_APP` + +## Getting Help + +- [Fly Community](https://community.fly.io) +- [Fly Docs](https://fly.io/docs) +- [Fly Status](https://status.fly.io) + diff --git a/partials/_firecracker_nav.html.erb b/partials/_firecracker_nav.html.erb index 18ea77f4fc..37c6e62a50 100644 --- a/partials/_firecracker_nav.html.erb +++ b/partials/_firecracker_nav.html.erb @@ -10,6 +10,7 @@ { text: "Deep Dive Demo App", path: "/docs/deep-dive/" }, { text: "Choose a Language or Framework", path: "/docs/getting-started/get-started-by-framework/" }, { text: "Fly.io Essentials", path: "/docs/getting-started/essentials/" }, + { text: "Migrate from Heroku", path: "/docs/getting-started/migrate-from-heroku/" }, { text: "Troubleshoot Deployments", path: "/docs/getting-started/troubleshooting/" } ] }, diff --git a/rails/getting-started/migrate-from-heroku.html.md b/rails/getting-started/migrate-from-heroku.html.md deleted file mode 100644 index d91b7e899b..0000000000 --- a/rails/getting-started/migrate-from-heroku.html.md +++ /dev/null @@ -1,338 +0,0 @@ ---- -title: Migrate from Heroku -layout: framework_docs -order: 2 ---- - -
- Illustration by Annie Ruygt of a bird carrying an app to a balloon who is welcoming the app to join his group -
- -This guide runs you through how to migrate a basic Rails application off of Heroku and onto Fly. It assumes you're running the following services on Heroku: - -* Puma web server -* Postgres database -* Redis in non-persistent mode -* Custom domain -* Background worker, like Sidekiq - -If your application is running with more services, additional work may be needed to migrate your application off Heroku. - -## Migrating your app - -The steps below run you through the process of migrating your Rails app from Heroku to Fly. - -### Provision and deploy Rails app to Fly - -From the root of the Rails app you're running on Heroku, run `fly launch` and -select the options to provision a new Postgres database, and optionally a Redis -database if you make use of Action Cable, caching, and popular third party gems -like Sidekiq. - - -```cmd -fly launch -``` -```output -Creating app in ~/list -Scanning source code -Detected a Rails app -? Choose an app name (leave blank to generate one): list -? Select Organization: John Smith (personal) -? Choose a region for deployment: Ashburn, Virginia (US) (iad) -Created app list in organization personal -Admin URL: https://fly.io/apps/list -Hostname: list.fly.dev -Set secrets on list: RAILS_MASTER_KEY -? Would you like to set up a Postgresql database now? Yes -For pricing information visit: https://fly.io/docs/about/pricing/#postgresql-clu -? Select configuration: Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk -Creating postgres cluster in organization personal - -. . . - -Postgres cluster list-db is now attached to namelist -? Would you like to set up an Upstash Redis database now? Yes -? Select an Upstash Redis plan Free: 100 MB Max Data Size - -Your Upstash Redis database namelist-redis is ready. - -. . . - - create Dockerfile - create .dockerignore - create bin/docker-entrypoint - create config/dockerfile.yml -Wrote config file fly.toml - -Your Rails app is prepared for deployment. - -Before proceeding, please review the posted Rails FAQ: -https://fly.io/docs/rails/getting-started/dockerfiles/. - -Once ready: run 'fly deploy' to deploy your Rails app. -``` - -It is worth heeding the advice at the end of this: -Before proceeding, please review the posted Rails FAQ: -[https://fly.io/docs/rails/getting-started/dockerfiles/](https://fly.io/docs/rails/getting-started/dockerfiles/). - -After the application is provisioned, deploy it by running: - -```cmd -fly deploy -``` - -When that's done, view your app in a browser: - -```cmd -fly apps open -``` - -There's still work to be done to move more Heroku stuff over, so don't worry if the app doesn't boot right away. There's a few commands that you'll find useful to configure your environment: - -* `fly logs` - Read error messages and stack traces emitted by your Rails application. -* `fly ssh console --pty -C "/rails/bin/rails console"` - Launches a Rails shell, which is useful to interactively test components of your Rails application. - -### Transfer Heroku secrets - -To see all of your Heroku env vars and secrets, run: - -```cmd -heroku config -s | grep -v -e "RAILS_MASTER_KEY" -e "DATABASE_URL" -e "REDIS_URL" -e "REDIS_TLS_URL" | fly secrets import -``` - -This command exports the Heroku secrets, excluding `RAILS_MASTER_KEY`, `DATABASE_URL` `REDIS_URL`, and `REDIS_TLS_URL`, and imports them into Fly. - -Verify your Heroku secrets are in Fly. - -```cmd -fly secrets list -NAME DIGEST CREATED AT -DATABASE_URL 24e455edbfcf1247a642cdae30e14872 14m29s ago -LANG 95a7bb7a8d0ee402edde95bb78ef95c7 1m24s ago -RACK_ENV fd89784e59c72499525556f80289b2c7 1m26s ago -RAILS_ENV fd89784e59c72499525556f80289b2c7 1m26s ago -RAILS_LOG_TO_STDOUT a10311459433adf322f2590a4987c423 1m25s ago -RAILS_SERVE_STATIC_FILES a10311459433adf322f2590a4987c423 1m23s ago -REDIS_TLS_URL b30fe87493e14d9b670dc0263dc935c9 1m25s ago -REDIS_URL 4583a46e747696319573e8bfbd0db04d 1m21s ago -SECRET_KEY_BASE 5afb43c2ddbba6c02ffa7e2834689692 1m22s ago -``` - -### Transfer the Database - - - -Set the `HEROKU_DATABASE_URL` variable in your Fly environment. - -```cmd -fly secrets set HEROKU_DATABASE_URL=$(heroku config:get DATABASE_URL) -``` - -Alright, lets start the transfer remotely on the Fly instance. - -```cmd -fly ssh console -``` - -Then from the remote Fly SSH console transfer the database. - -```cmd -pg_dump -Fc --no-acl --no-owner -d $HEROKU_DATABASE_URL | pg_restore --verbose --clean --no-acl --no-owner -d $DATABASE_URL -``` - -You may need to upgrade your Heroku database to match the version of the source Fly database. Refer to Heroku's [Upgrading the Version of a Heroku Postgres Database](https://devcenter.heroku.com/articles/upgrading-heroku-postgres-databases) for instructions on how to upgrade, then try the command above again. - -After the database transfers unset the `HEROKU_DATABASE_URL` variable. - -```cmd -fly secrets unset HEROKU_DATABASE_URL -``` - -Then launch your Heroku app to see if its running. - -``` -fly apps open -``` - -If you have a Redis server, there's a good chance you need to set that up. - -### Multiple processes & background workers - -Heroku uses Procfiles to describe multi-process Rails applications. Fly describes multi-processes with the [`[processes]` directive](/docs/reference/configuration/#the-processes-section) in the `fly.toml` file. - -If your Heroku `Procfile` looks like this: - -```Procfile -web: bundle exec puma -C config/puma.rb -worker: bundle exec sidekiq -release: rails db:migrate -``` - -Move everything except for the `release:` line to your `fly.toml` file: - -```toml -[processes] -web = "bundle exec puma -C config/puma.rb" -worker = "bundle exec sidekiq" -``` - -If you have a `release:` line in your Heroku Procfile, that will listed separately in your `fly.toml` file: - -```toml -[deploy] - release_command = "bin/rails db:migrate" -``` - -You will also want to prevent your release command from also being -run during the deploy step. To do so, regenerate your dockerfile using: - -```shell -$ bin/rails generate dockerfile --no-prepare -``` - -Next, under the `[[services]]` directive, find the entry that maps to `internal_port = 8080`, and add `processes = ["web"]`. The configuration file should look something like this: - -```toml -[[services]] - processes = ["web"] # this service only applies to the web process - http_checks = [] - internal_port = 8080 - protocol = "tcp" - script_checks = [] -``` - -This associates the process with the service that Fly launches. Save these changes and run the deploy command. - -```cmd -fly deploy -``` - -You should see a `web` and `worker` process deploy. - -### Custom Domain & SSL Certificates - -After you finish deploying your application to Fly and have tested it extensively, [read through the Custom Domain docs](/docs/app-guides/custom-domains-with-fly) and point your domain at Fly. - -In addition to supporting [`CNAME` DNS records](/docs/app-guides/custom-domains-with-fly#option-i-cname-records), Fly also supports [`A` and `AAAA` records](/docs/app-guides/custom-domains-with-fly#option-ii-a-and-aaaa-records) for those who want to point `example.com` (without the `www.example.com`) directly at Fly. - -## Cheat Sheet - -Old habits die hard, especially good habits like deploying frequently to production. Below is a quick overview of the differences you'll notice initially between Fly and Heroku. - -### Commands - -Fly commands are a bit different than Heroku, but you'll get use to them after a few days. - -| Task | Heroku | Fly | -|--------------|-----------|------------| -| Deployments | `git push heroku` | `fly deploy` | -| Rails console | `heroku console` | `fly ssh console --pty -C "/app/bin/rails console"` | -| Database migration | `heroku rake db:migrate` | `fly ssh console -C "/app/bin/rake db:migrate"` | -| Postgres console | `heroku psql` | `fly postgres connect -a ` -| Tail log files | `heroku logs` | `fly logs` | -| View configuration | `heroku config` | `fly ssh console -C "printenv"` | -| View releases | `heroku releases` | `fly releases` | -| Help | `heroku help` | `fly help` | - -Check out the [Fly CLI docs](https://fly.io/docs/flyctl/) for a more extensive inventory of Fly commands. - -### Deployments - -By default Heroku deployments are kicked off via the `git push heroku` command. Fly works a bit differently by kicking of deployments via `fly deploy`—git isn't needed to deploy to Fly. The advantage to this approach is your git history will be clean and not full of commits like `git push heroku -am "make app work"` or `git push heroku -m "ok it will really work this time"`. - -To achieve the desired `git push` behavior, we recommend setting up `fly deploy` as the final command in your continuous integration pipeline, as outlined for GitHub in the [Continuous Deployment with Fly and GitHub Actions](https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/) docs. - -#### Release phase tasks - -Heroku has a `release: rake db:migrate` command in their Procfiles to run tasks while the application is deployed. Rails 7.1 will include a `bin/rails db:prepare` in the list of commands to be run on deploy in their `bin/docker-entrypoint` file. Fly.io supports both approaches. - -If you don't want to run migrates by default per release, delete the prequite but leave the `:release` task. You'll be able to manually run migrations on Fly via `fly ssh console -C "/app/bin/rails db:migrate"`. - -#### Deploy via git - -Heroku's default deployment technique is via `git push heroku`. Fly doesn't require a git commit, just run `fly deploy` and the files on your local workstation will be deployed. - -Fly can be configured to deploy on git commits with the following techniques with a [GitHub Action](https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/). - -### Databases - -Fly and Heroku have different Postgres database offerings. The most important distinction to understand about using Fly is that it automates provisioning, maintenance, and snapshot tasks for your Postgres database, but it does not manage it. If you run out of disk space, RAM, or other resources on your Fly Postgres instances, you'll have to scale those virtual machines from the [Fly CLI](https://fly.io/docs/reference/postgres/). - -Contrast that with Heroku, which fully manages your database and includes an extensive suite of tools to provision, backup, snapshot, fork, patch, upgrade, and scale up/down your database resources. - -The good news for people who want a highly managed Postgres database is they can continue hosting it at Heroku and point their Fly instances to it! - -#### Heroku's managed database - -One command is all it takes to point Fly apps at your Heroku managed database. - -```cmd -fly secrets set DATABASE_URL=$(heroku config:get DATABASE_URL) -``` - -This is a great way to get comfortable with Fly if you prefer a managed database provider. In the future if you decide you want to migrate your data to Fly, you can do so [pretty easily with a few commands](#transfer-the-database). - - -#### Fly's databases - -The most important thing you'll want to be comfortable with using Fly's database offering is [backing up and restoring your database](/docs/rails/the-basics/backup-and-restoring-data/). - -As your application grows, you'll probably first [scale disk and RAM resources](/docs/reference/postgres/#scaling-vertically-adding-more-vm-resources), then [scale out with multiple replicas](/docs/reference/postgres/#scaling-horizontally-adding-more-replicas). Common maintenance tasks will include [upgrading Postgres](/docs/reference/postgres/#upgrading-the-postgres-app) as new versions are released with new features and security updates. - -[You Postgres, now what?](/docs/reference/postgres-whats-next/) is a more comprehensive guide for what's required when running your Postgres databases on Fly. - -### Pricing - -Heroku and Fly have very different pricing structures. You'll want to read through the details on [Fly's pricing page](/docs/about/pricing/) before launching to production. The sections below serve as a rough comparison between Heroku and Fly's plans as of August 2022. - -
- Please do your own comparison of plans before switching from Heroku to Fly. The examples below are illustrative estimates between two very different offerings, which focuses on the costs of app & database servers. It does not represent the final costs of each plan. Also, the prices below may not be immediately updated if Fly or Heroku change prices. -
- -#### Free Plans - -Heroku will not offer free plans as of November 28, 2022. - -Fly offers free usage for up to 3 full time VMs with 256MB of RAM, which is enough to run a tiny Rails app and Postgres database to get a feel for how Fly works. - -#### Plans for Small Rails Apps - -Heroku's Hobby tier is limited to 10,000 rows of data, which gets exceeded pretty quickly requiring the purchase of additional rows of data. - -| Heroku Resource | Specs | Price | -|----------|------|-------| -| App Dyno | 512MB RAM | $7/mo | -| Database | 10,000,000 rows | $9/mo | -| **Estimated cost** | | $16/mo | - -Fly's pricing is [metered for the resources](https://fly.io/docs/about/pricing/) you use. Database is billed by the amount of RAM and disk space used, not by rows. The closest equivalent to the Heroku Hobby tier on Fly looks like this: - -| Fly Resource | Specs | Price | -|----------|------|-------| -| App Server | 1GB RAM | ~$5.70/mo | -| Database Server | 256MB RAM / 10Gb disk | ~$3.44/mo | -| **Estimated cost** | | ~$9.14/mo | - -#### Plans for Medium to Large Rails Apps - -There's too many variables to compare Fly and Heroku's pricing for larger Rails applications depending on your needs, so you'll definitely want to do your homework before migrating everything to Fly. This comparison focuses narrowly on the costs of app & database resources, and excludes other factors such as bandwidth costs, bundled support, etc. - -| Heroku Resource | Specs | Price | Quantity | Total | -|----------|-------|-------|----------|--------| -| App Dyno | 2.5GB RAM | $250/mo | 8 | $2,000/mo | -| Database | 61GB RAM / 1TB disk | $2,500/mo | 1 | $2,500/mo | -| **Estimated cost** | | | | $4,500/mo | - -Here's roughly the equivalent resources on Fly: - -| Fly Resource | Specs | Price | Quantity | Total | -|----------|-------|-------|----------|--------| -| App Server | 4GB RAM / 2X CPU | ~$62.00/mo | 8 | ~$496/mo | -| Database Server | 64GB RAM / 500GB disk | ~$633/mo | 2 | ~$1,266/mo | -| **Estimated cost** | | | | ~$1,762/mo | - -Again, the comparison isn't realistic because it focuses only on application and database servers, but it does give you an idea of how the different cost structures scale on each platform. For example, Heroku's database offering at this level is redundant, whereas Fly offers 2 database instances to achieve similar levels of redundancy. From 477e6359c99982301eb2379c98fd31b66015a4d4 Mon Sep 17 00:00:00 2001 From: Sprite Date: Thu, 12 Feb 2026 09:34:55 +0000 Subject: [PATCH 2/3] Address PR review feedback on Heroku migration guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add illustration back (Annie Ruygt migrate.png) - Add pricing paragraph with link to fly.io/pricing - Add CLUSTER_ID reminders and fly mpg list fallback - Add warnings not to commit secrets files to git - Fix fly open → fly apps open - Fix [[services]] → [http_service] format - Remove invalid maintenance mode link Co-Authored-By: Claude Opus 4.6 --- getting-started/migrate-from-heroku.html.md | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/getting-started/migrate-from-heroku.html.md b/getting-started/migrate-from-heroku.html.md index c60c2d7d63..fa31802469 100644 --- a/getting-started/migrate-from-heroku.html.md +++ b/getting-started/migrate-from-heroku.html.md @@ -6,6 +6,12 @@ toc: true redirect_from: /docs/rails/getting-started/migrate-from-heroku/ --- +
+ Illustration by Annie Ruygt of a bird carrying an app to a balloon who is welcoming the app to join his group +
+ This guide will walk you through migrating your Heroku app to Fly.io. Just follow the steps in order. **What this guide covers:** @@ -18,6 +24,8 @@ This guide will walk you through migrating your Heroku app to Fly.io. Just follo **Time required:** Most apps can be migrated in under 30 minutes. +Unlike Heroku's dyno-based tiers, Fly.io uses pay-as-you-go pricing - you only pay for the compute, storage, and bandwidth you actually use. There are no fixed plan tiers to choose from. See [fly.io/pricing](https://fly.io/pricing) for current rates. + ## TL;DR - The Fast Path For experienced developers, here's the entire migration in 7 commands: @@ -25,6 +33,7 @@ For experienced developers, here's the entire migration in 7 commands: ```bash # 1. Export secrets (excluding Heroku-managed vars) heroku config -s -a YOUR_HEROKU_APP | grep -v '^DATABASE_URL\|^REDIS_URL\|^HEROKU_' > secrets.txt +# ⚠️ Don't commit secrets.txt to git! # 2. Create Fly app (generates Dockerfile automatically) cd your-app-directory @@ -32,7 +41,8 @@ fly launch --no-deploy # 3. Create Managed Postgres and import data fly mpg create --name your-db --region iad -# Note the CLUSTER_ID from output, then: +# ⚠️ Replace CLUSTER_ID below with the cluster ID from the output above. +# If you missed it, run: fly mpg list fly mpg proxy CLUSTER_ID --local-port 15432 & pg_dump --no-owner --no-acl "$(heroku config:get DATABASE_URL -a YOUR_HEROKU_APP)" | \ PGPASSWORD=YOUR_FLY_DB_PASSWORD psql -h localhost -p 15432 -U fly-user -d fly-db @@ -75,6 +85,7 @@ export HEROKU_APP="your-app-name" # Export environment variables (excluding Heroku-managed ones) heroku config -s -a $HEROKU_APP | grep -v '^HEROKU_' | grep -v '^DATABASE_URL' | grep -v '^REDIS_URL' > heroku-env.txt +# ⚠️ This file contains secrets - don't commit it to git! # See what add-ons you have heroku addons -a $HEROKU_APP @@ -144,10 +155,12 @@ Save the connection details it outputs! You'll see something like: Connection string: postgresql://fly-user:PASSWORD@pgbouncer.CLUSTER_ID.flympg.net/fly-db ``` +Note the cluster ID (the `CLUSTER_ID` part) - you'll need it for the next few commands. If you lose it, you can always find it with `fly mpg list`. + To import your data, use `fly mpg proxy` to create a local tunnel, then `pg_dump` and `psql`: ```bash -# Terminal 1: Start a proxy to your Fly database (use the cluster ID from the output above) +# Terminal 1: Start a proxy to your Fly database (replace CLUSTER_ID with your cluster ID) fly mpg proxy CLUSTER_ID --local-port 15432 # Terminal 2: Dump from Heroku and restore to Fly @@ -214,10 +227,9 @@ If your app has multiple process types (web + worker), edit your `fly.toml`: worker = "npm run worker" # The web process gets the HTTP service -[[services]] +[http_service] internal_port = 8080 processes = ["web"] - # ... rest of service config ``` For a release command (like database migrations), add: @@ -243,7 +255,7 @@ fly logs ```bash # Open your app in a browser -fly open +fly apps open # Check the status fly status @@ -284,7 +296,6 @@ Once DNS propagates, Fly automatically provisions an SSL certificate. | `heroku pg:psql` | `fly mpg connect CLUSTER_ID` | | `heroku restart` | `fly apps restart` | | `heroku scale web=2` | `fly scale count web=2` | -| `heroku maintenance:on` | See [maintenance mode docs](/docs/apps/maintenance/) | ## Common Add-on Replacements From ad28e8a22232cc59e260fc1ef7e8fbd3ce3714e3 Mon Sep 17 00:00:00 2001 From: Sprite Date: Thu, 12 Feb 2026 09:48:47 +0000 Subject: [PATCH 3/3] Add CLUSTER_ID reminder to fly mpg attach command Co-Authored-By: Claude Opus 4.6 --- getting-started/migrate-from-heroku.html.md | 1 + 1 file changed, 1 insertion(+) diff --git a/getting-started/migrate-from-heroku.html.md b/getting-started/migrate-from-heroku.html.md index fa31802469..88a9f62d03 100644 --- a/getting-started/migrate-from-heroku.html.md +++ b/getting-started/migrate-from-heroku.html.md @@ -178,6 +178,7 @@ You'll see some errors about Heroku-specific extensions (pg_stat_statements, eve Attach the database to your app: ```bash +# Replace CLUSTER_ID with the cluster ID from fly mpg create (or run fly mpg list) fly mpg attach CLUSTER_ID -a your-app-name ```