How to Migrate from Heroku to AWS/Railway/Fly.io

Type: Software Reference Confidence: 0.92 Sources: 8 Verified: 2026-02-23 Freshness: monthly

TL;DR

Constraints

Quick Reference

Heroku Feature AWS Equivalent Railway Fly.io
Web Dynos ECS Fargate tasks Railway services Fly Machines
Worker Dynos ECS tasks (separate) Separate services Process groups
Heroku Postgres Amazon RDS Railway Postgres Fly Managed Postgres
Heroku Redis ElastiCache Railway Redis Upstash Redis
Config Vars SSM / Secrets Manager Railway variables fly secrets
Heroku Pipelines CodePipeline + CodeBuild Environments fly deploy
git push heroku Push to ECR → ECS git push (auto) fly deploy
Procfile ECS task definition Procfile (native) Procfile / fly.toml
Auto-scaling ECS Service Auto Scaling Automatic (vertical) Machines auto-start/stop
Logging CloudWatch Logs Built-in dashboard fly logs
SSL/TLS ACM + ALB listener Automatic (Let's Encrypt) Automatic (Let's Encrypt)
Free tier 12 months free + always-free Hobby $5/mo ($5 credit) 3 shared VMs, 160GB BW

Decision Tree

START
+-- What matters most?
|   +-- Full control + AWS -> AWS ECS Fargate [src1, src2]
|   +-- Developer simplicity -> Railway [src3]
|   +-- Global edge deploy -> Fly.io [src4]
+-- Team size?
|   +-- Solo/small -> Railway or Fly.io (less ops)
|   +-- DevOps team -> AWS (more powerful)
+-- Database size?
|   +-- < 10 GB -> pg_dump/restore [src1]
|   +-- > 10 GB -> AWS DMS live replication [src2]
+-- Downtime tolerance?
|   +-- Zero downtime -> AWS DMS + blue-green deploy [src2, src6]
|   +-- Maintenance window OK -> pg_dump/restore (any platform)
+-- DEFAULT -> Containerize first, then pick platform

Step-by-Step Guide

1. Containerize your app

Convert Procfile-based app to Docker. [src1, src5]

docker build -t myapp .
docker run -p 3000:3000 -e PORT=3000 myapp

2. Export Heroku configuration

Capture config vars, add-ons, and database info. [src1, src4]

heroku config -a myapp --shell > heroku_env.txt
heroku addons -a myapp
heroku pg:info -a myapp

3a. Migrate to AWS ECS Fargate

Full AWS ecosystem integration. Requires platform v1.4.0+. [src1, src2, src7]

# Push to ECR, create cluster, create service
aws ecr create-repository --repository-name myapp
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/myapp:latest
aws ecs create-service --cluster myapp --service-name myapp \
  --launch-type FARGATE --platform-version LATEST

3b. Migrate to Railway

Closest to Heroku's DX. Supports Procfile natively. [src3, src8]

railway init && railway link && railway up

3c. Migrate to Fly.io

Best for global edge. Free allowance: 3 shared VMs. [src4]

fly launch && fly deploy

4. Migrate the database

Always use --no-owner --no-acl to avoid role mismatches. [src1, src2, src5]

pg_dump $(heroku config:get DATABASE_URL -a myapp) -Fc -f backup.dump
pg_restore -h target-host -d myapp --no-owner --no-acl backup.dump

5. Update DNS and cut over

Point domain to the new platform. Fly.io dedicated IPv4 costs $2/mo. [src1, src4]

# AWS: CNAME -> ALB
# Railway: railway domain add myapp.com
# Fly.io: fly certs add myapp.com
dig myapp.com  # Verify propagation

Code Examples

Terraform: AWS ECS Fargate for Heroku migration

Full script: terraform-aws-ecs-fargate-infrastructure-for-herok.txt (59 lines)

resource "aws_ecs_task_definition" "app" {
  family                   = "myapp"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"
  container_definitions    = jsonencode([{
    name  = "myapp"
    image = "${aws_ecr_repository.app.repository_url}:latest"
    portMappings = [{ containerPort = 3000 }]
  }])
}

Bash: Complete Heroku-to-Railway migration

Full script: bash-complete-heroku-to-railway-migration-script.sh (32 lines)

#!/bin/bash
set -euo pipefail
HEROKU_APP="$1"
railway init && railway link
# ... (see full script)

GitHub Actions: CI/CD for ECS Fargate deployment

# Input:  GitHub repo with Dockerfile
# Output: Auto-deploy to ECS on push to main
name: Deploy to ECS
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
      - uses: aws-actions/amazon-ecr-login@v2
      - run: docker build -t $ECR_REGISTRY/myapp:$GITHUB_SHA . && docker push ...
      - uses: aws-actions/amazon-ecs-deploy-task-definition@v2

Anti-Patterns

Wrong: Storing files on local filesystem

// ❌ BAD — file disappears on restart
fs.writeFileSync('uploads/file.txt', data);

Correct: Use S3/R2 object storage

// ✅ GOOD — persistent across deploys [src1]
await s3.send(new PutObjectCommand({ Bucket: 'myapp', Key: 'file.txt', Body: data }));

Wrong: Hardcoding Heroku-specific env vars

# ❌ BAD — Heroku-managed variable won't exist
REDIS_URL="$HEROKU_REDIS_URL"

Correct: Platform-agnostic configuration

# ✅ GOOD — standard env vars, set per platform [src1, src3]
DATABASE_URL=$(aws ssm get-parameter --name /myapp/database-url --with-decryption --query Parameter.Value --output text)

Wrong: pg_restore without --no-owner

# ❌ BAD — Heroku roles don't exist on target
pg_restore -h rds-host -U postgres -d myapp backup.dump
# ERROR: role "uf3kxyz" does not exist

Correct: Strip ownership during restore

# ✅ GOOD — ignore Heroku-specific roles [src2, src5]
pg_restore -h rds-host -U postgres -d myapp --no-owner --no-acl backup.dump

Common Pitfalls

Diagnostic Commands

# Heroku: capture state before migration
heroku ps -a myapp && heroku config -a myapp --shell && heroku addons -a myapp

# AWS: verify
aws ecs describe-services --cluster myapp-cluster --services myapp-service
aws ecs list-tasks --cluster myapp-cluster
aws logs tail /ecs/myapp --follow

# Railway: verify
railway status && railway logs && railway domain list

# Fly.io: verify
fly status && fly logs && fly checks list

Version History & Compatibility

Platform Key Feature Heroku Equivalent Cost Model (2026)
AWS ECS Fargate Serverless containers, full AWS ecosystem Dynos + add-ons Pay-per-vCPU-second (~50–70% cheaper at scale)
Railway Git-push deploys, auto-scaling, project canvas Almost identical to Heroku Hobby $5/mo, Pro $20/seat/mo
Fly.io Edge deployment, global distribution Dynos + multi-region Free (3 VMs, 160GB BW) + usage
Render Static sites + services Heroku alternative Free tier + pay-per-use

When to Use / When Not to Use

Migrate When Don't Migrate When Consider Instead
Costs exceed $500/mo Simple app under $50/mo Stay on Heroku Eco
Need auto-scaling beyond Heroku No DevOps experience Railway (Heroku-like)
Compliance requires specific cloud Timeline < 2 weeks Plan longer
Need VPC networking / private subnets App is in prototype/MVP phase Stay on Heroku or Railway
Need multi-region deployment Single-region, low traffic Any platform works

Important Caveats

Related Units