---
# === IDENTITY ===
id: software/migrations/heroku-to-aws/2026
canonical_question: "How do I migrate from Heroku to AWS/Railway/Fly.io?"
aliases:
  - "Heroku to AWS migration guide"
  - "migrate Heroku app to AWS ECS"
  - "Heroku alternative migration"
  - "Heroku to Railway migration"
  - "Heroku to Fly.io migration"
  - "move from Heroku to cloud"
  - "Heroku to ECS Fargate migration"
  - "Heroku alternatives 2026"
entity_type: software_reference
domain: software > migrations > heroku_to_aws
region: global
jurisdiction: global
temporal_scope: 2022-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.93
version: 2.1
first_published: 2026-02-17

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: "Heroku entered sustaining engineering mode (2026-02-06) — no new features, no new Enterprise contracts for new customers"
  next_review: 2026-11-13
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "AWS Fargate requires platform version 1.4.0+ (Linux) — all earlier versions retired as of 2024"
  - "Heroku DATABASE_URL includes sslmode=require — strip or remap SSL params for RDS/Railway/Fly Postgres"
  - "Docker images must listen on $PORT — all three target platforms inject PORT at runtime"
  - "Never run pg_restore against a production database without --no-owner --no-acl — role mismatches break the restore"
  - "Railway Hobby plan ($5/mo) enforces a single-user limit — teams must use the Pro plan ($20/mo)"
  - "Fly.io persistent volumes are region-pinned — you cannot move a volume to a different region after creation"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User wants to migrate from Heroku to Google Cloud (GCP) or Azure"
    use_instead: "Search for platform-specific migration guide (GCP Cloud Run, Azure Container Apps)"
  - condition: "User wants to containerize an existing app without changing platforms"
    use_instead: "software/devops/docker-containerization-guide/2026"
  - condition: "User is comparing PaaS platforms without migrating"
    use_instead: "software/devops/paas-comparison-guide/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: target_platform
    question: "Which target platform are you migrating to?"
    type: choice
    options: ["AWS ECS Fargate", "Railway", "Fly.io", "Not sure yet"]
  - key: database_size
    question: "How large is your Heroku Postgres database?"
    type: choice
    options: ["Small (<1 GB)", "Medium (1-10 GB)", "Large (>10 GB)"]
  - key: downtime_tolerance
    question: "Can the app tolerate migration downtime?"
    type: choice
    options: ["Yes — maintenance window OK", "No — need near-zero downtime"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/migrations/heroku-to-aws/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-05-17)"

# === RELATED UNITS ===
related_kos:
  depends_on:
    - id: "software/devops/docker-containerization-guide/2026"
      label: "Docker Containerization Guide"
  related_to:
    - id: "software/migrations/docker-compose-to-kubernetes/2026"
      label: "Docker Compose to Kubernetes Migration"
    - id: "software/migrations/monolith-to-microservices/2026"
      label: "Monolith to Microservices Migration"
  solves:
    - id: "software/migrations/jenkins-to-github-actions/2026"
      label: "Jenkins to GitHub Actions Migration"
  alternative_to:
    - id: "software/devops/paas-comparison-guide/2026"
      label: "PaaS Comparison Guide"

# === SOURCES (5-8 authoritative sources) ===
sources:
  - id: src1
    title: "How to Migrate from Heroku to AWS"
    author: OneUptime
    url: https://oneuptime.com/blog/post/2026-02-12-migrate-from-heroku-to-aws/view
    type: technical_blog
    published: 2026-02-12
    reliability: high
  - id: src2
    title: "Heroku to AWS Migration: 2025 Guide to a Seamless Moving"
    author: Cloudvisor
    url: https://cloudvisor.co/heroku-to-aws-migration/
    type: technical_blog
    published: 2025-06-01
    reliability: high
  - id: src3
    title: "Railway Migration Guide from Heroku"
    author: Railway
    url: https://docs.railway.com/guides/migrate-from-heroku
    type: official_docs
    published: 2025-04-01
    reliability: authoritative
  - id: src4
    title: "Migrate from Heroku — Fly.io Docs"
    author: Fly.io
    url: https://fly.io/docs/getting-started/migrate-from-heroku/
    type: official_docs
    published: 2025-09-01
    reliability: authoritative
  - id: src5
    title: "Moving from Heroku to AWS: A Practical Guide for Migration"
    author: NJC Labs
    url: https://njclabs.com/blog/heroku-to-aws-migration-guide/
    type: technical_blog
    published: 2025-11-01
    reliability: high
  - id: src6
    title: "Outgrowing Heroku: How TeePublic Conquered Black Friday on Amazon ECS"
    author: Judoscale
    url: https://judoscale.com/blog/heroku-to-aws-migration
    type: technical_blog
    published: 2025-07-10
    reliability: moderate_high
  - id: src7
    title: "AWS Fargate Platform Versions — Amazon ECS Docs"
    author: AWS
    url: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/platform-fargate.html
    type: official_docs
    published: 2025-12-15
    reliability: authoritative
  - id: src8
    title: "Railway Pricing & Plans (2026)"
    author: Railway
    url: https://docs.railway.com/pricing
    type: official_docs
    published: 2025-03-03
    reliability: authoritative
  - id: src9
    title: "Heroku Enters Sustaining Engineering Mode: What It Means and When to Consider Alternatives"
    author: DeployHQ
    url: https://www.deployhq.com/blog/heroku-sustaining-engineering-alternatives
    type: technical_blog
    published: 2026-02-10
    reliability: high
---

# How to Migrate from Heroku to AWS/Railway/Fly.io

## TL;DR

- **Bottom line**: Containerize your Heroku app with Docker, choose a target platform (AWS ECS Fargate for full control, Railway for simplicity, Fly.io for edge deployment), migrate your database with `pg_dump`/`pg_restore`, and set up CI/CD to replace Heroku's `git push` workflow.
- **Key tool/command**: `docker build -t myapp . && docker push <registry>/myapp` -- containerization is the foundation for all three migration paths.
- **Watch out for**: Heroku's ephemeral filesystem -- any file writes (uploads, temp files) will fail on all three platforms without switching to S3 or equivalent object storage.
- **Works with**: Any Heroku app (web dynos, workers, scheduled jobs); target platforms: AWS ECS Fargate (platform v1.4.0+), Railway (Hobby/Pro), Fly.io (Machines).

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- AWS Fargate requires platform version 1.4.0+ (Linux) -- all earlier versions were retired in 2024. Always specify `platformVersion: "1.4.0"` or `LATEST` in task definitions. [src7]
- Heroku `DATABASE_URL` includes `sslmode=require` -- you must strip or remap SSL parameters when connecting to RDS (use `rds-ca-bundle`), Railway (auto-managed), or Fly Postgres. [src1, src2]
- Docker images must listen on `$PORT` -- all three target platforms inject the PORT environment variable at runtime; hardcoding a port number will cause health check failures. [src1, src4]
- Never run `pg_restore` against a production database without `--no-owner --no-acl` flags -- Heroku role names don't exist on the target and will cause the restore to fail. [src2, src5]
- Railway Hobby plan ($5/mo) is single-user only with $5 of included usage -- teams must upgrade to Pro ($20/seat/mo with $20 included usage). As of March 2025, egress and volume pricing were reduced. [src3, src8]
- Fly.io persistent volumes are region-pinned -- once created, a volume cannot be moved to another region. Match your database volume region to your primary compute region. [src4]

## Quick Reference

| Heroku Feature | AWS Equivalent | Railway Equivalent | Fly.io Equivalent |
|---|---|---|---|
| Web Dynos | ECS Fargate tasks | Railway services | Fly Machines |
| Worker Dynos | ECS Fargate tasks (separate service) | Railway services (separate) | Fly Machines (process groups) |
| Heroku Postgres | Amazon RDS for PostgreSQL | Railway Postgres | Fly Managed Postgres |
| Heroku Redis | Amazon ElastiCache for Redis | Railway Redis | Upstash Redis (integration) |
| Config Vars | SSM Parameter Store / Secrets Manager | Railway variables | Fly Secrets (`fly secrets set`) |
| Heroku Pipelines | AWS CodePipeline + CodeBuild | Railway environments | Fly deploy (`fly deploy`) |
| `git push heroku` | Push to ECR -> ECS deploy | `git push` (auto-deploy) | `fly deploy` |
| Add-ons | AWS Marketplace / third-party | Railway plugins | Fly extensions |
| Heroku Scheduler | EventBridge + Lambda/ECS | Railway cron services | Fly Machines (scheduled) |
| Review Apps | CodeBuild + temp environments | Railway preview environments | Fly preview (`fly deploy --app preview`) |
| Procfile | ECS task definition | Procfile (supported natively) | Procfile or `fly.toml` |
| Free tier | Free tier (12 months) + always-free | Hobby plan ($5/mo, $5 credit) | Trial only (2 VM-hrs / 7 days, then pay-as-you-go) |
| Auto-scaling | ECS Service Auto Scaling (target tracking) | Automatic vertical scaling | Fly Machines auto-start/stop |
| Logging | CloudWatch Logs | Built-in logs dashboard | `fly logs` (built-in) |
| SSL/TLS | ACM + ALB listener | Automatic (Let's Encrypt) | Automatic (Let's Encrypt) |

## Decision Tree

```
START
+-- What matters most?
|   +-- Full control + AWS ecosystem -> AWS ECS Fargate [src1, src2]
|   +-- Developer simplicity (Heroku-like) -> Railway [src3]
|   +-- Global edge deployment + low latency -> Fly.io [src4]
+-- What's your team size?
|   +-- Solo / small team -> Railway or Fly.io (less ops overhead)
|   +-- DevOps team available -> AWS (more powerful, more complex)
+-- Database size?
|   +-- < 10 GB -> pg_dump/pg_restore (simple, minutes of downtime) [src1]
|   +-- > 10 GB -> AWS DMS for live replication (near-zero downtime) [src2]
+-- Do you need auto-scaling?
|   +-- YES -> AWS ECS Fargate (target tracking) or Railway (automatic) [src1, src3]
|   +-- NO -> Any platform works
+-- Budget constraint?
|   +-- Minimal -> Railway ($5/mo hobby) or Fly.io (free allowance)
|   +-- Flexible -> AWS (pay-as-you-go, most cost-effective at scale) [src2]
+-- Downtime tolerance?
|   +-- Zero downtime required -> AWS DMS + blue-green deploy [src2, src6]
|   +-- Maintenance window OK -> pg_dump/pg_restore (any platform) [src1]
+-- DEFAULT -> Start with Docker containerization, then pick platform
```

## Step-by-Step Guide

### 1. Containerize your Heroku application

Convert your Procfile-based app to a Docker container. This is the foundation for migrating to any platform. [src1, src5]

```dockerfile
# Dockerfile for a Node.js app (adapt for your language)
FROM node:20-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 3000
# Heroku uses $PORT; all three platforms also set $PORT
CMD ["node", "server.js"]
```

```bash
# Build and test locally
docker build -t myapp .
docker run -p 3000:3000 -e PORT=3000 myapp
```

**Verify**: App runs at `http://localhost:3000` with same behavior as Heroku.

### 2. Export Heroku configuration

Capture all config vars, add-ons, and database connection strings before migrating. [src1, src4]

```bash
# Export all config vars
heroku config -a myapp --shell > heroku_env.txt

# List add-ons to identify services to migrate
heroku addons -a myapp

# Get database connection string
heroku pg:info -a myapp
heroku config:get DATABASE_URL -a myapp

# Capture current dyno formation
heroku ps -a myapp
```

**Verify**: `heroku_env.txt` contains all non-Heroku-managed variables.

### 3a. Migrate to AWS ECS Fargate

The most powerful option with full AWS ecosystem integration. Requires Fargate platform version 1.4.0+ (LATEST recommended). [src1, src2, src7]

```bash
# Step 1: Push Docker image to ECR
aws ecr create-repository --repository-name myapp
aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
docker tag myapp:latest <account-id>.dkr.ecr.<region>.amazonaws.com/myapp:latest
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/myapp:latest

# Step 2: Create ECS cluster
aws ecs create-cluster --cluster-name myapp-cluster

# Step 3: Create task definition (save as task-def.json)
# Map your Procfile web process to containerDefinitions
# Map Heroku config vars to environment/secrets in task definition
# IMPORTANT: Specify platformVersion "1.4.0" or "LATEST"

# Step 4: Create service with load balancer
aws ecs create-service \
  --cluster myapp-cluster \
  --service-name myapp-service \
  --task-definition myapp:1 \
  --desired-count 2 \
  --launch-type FARGATE \
  --platform-version LATEST \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}"
```

**Verify**: `aws ecs describe-services --cluster myapp-cluster --services myapp-service` shows `RUNNING`.

### 3b. Migrate to Railway

The simplest path -- closest to Heroku's developer experience. Railway supports Procfile natively and auto-deploys from GitHub. [src3, src8]

```bash
# Step 1: Install Railway CLI
npm install -g @railway/cli

# Step 2: Login and create project
railway login
railway init

# Step 3: Connect GitHub repo (auto-deploy on push)
railway link

# Step 4: Import Heroku environment variables
# Railway supports Heroku variable import in the dashboard
# Or set manually:
cat heroku_env.txt | while IFS='=' read key value; do
  railway variables set "$key=$value"
done

# Step 5: Add database
railway add --plugin postgresql

# Step 6: Deploy
railway up
# Or just git push (auto-deploys from GitHub)
```

**Verify**: `railway status` shows deployment is live. Check logs: `railway logs`.

### 3c. Migrate to Fly.io

Best for globally distributed apps with edge deployment. Note: Fly.io retired its free Hobby/Launch/Scale plans in 2024 — new signups get a 2-VM-hour / 7-day trial, then pay-as-you-go. [src4]

```bash
# Step 1: Install flyctl
curl -L https://fly.io/install.sh | sh

# Step 2: Launch app (creates fly.toml)
fly launch
# Select region, create Postgres if needed

# Step 3: Import secrets from Heroku
cat heroku_env.txt | while IFS='=' read key value; do
  fly secrets set "$key=$value"
done

# Step 4: Create managed Postgres
fly postgres create --name myapp-db
fly postgres attach myapp-db

# Step 5: Deploy
fly deploy

# Step 6: Scale to multiple regions (optional)
fly scale count 2 --region iad,cdg
```

**Verify**: `fly status` shows app running. Check: `fly open`.

### 4. Migrate the database

Transfer data from Heroku Postgres to the target platform. Always use `--no-owner --no-acl` to avoid role mismatch errors. [src1, src2, src5]

```bash
# METHOD 1: pg_dump/pg_restore (simple, brief downtime)
# Enable maintenance mode on Heroku
heroku maintenance:on -a myapp

# Dump from Heroku
heroku pg:backups:capture -a myapp
heroku pg:backups:download -a myapp
# Or direct dump:
pg_dump $(heroku config:get DATABASE_URL -a myapp) -Fc -f heroku_backup.dump

# Restore to target (use --no-owner --no-acl to avoid role errors)
# AWS RDS:
pg_restore -h mydb.xxx.rds.amazonaws.com -U postgres -d myapp --no-owner --no-acl heroku_backup.dump
# Railway: use the provided DATABASE_URL
pg_restore $RAILWAY_DATABASE_URL -d railway --no-owner --no-acl heroku_backup.dump
# Fly.io:
fly proxy 15432:5432 -a myapp-db &
pg_restore -h localhost -p 15432 -U postgres -d myapp --no-owner --no-acl heroku_backup.dump

# METHOD 2: AWS DMS (live replication, near-zero downtime)
# Set up DMS replication task: Heroku Postgres -> RDS
# Full load + CDC, then cutover when in sync [src2, src6]
```

**Verify**: Compare row counts between source and target for critical tables.

### 5. Update DNS and cut over

Point your domain to the new platform and decommission Heroku. [src1, src4]

```bash
# AWS: Point domain to ALB
# Add CNAME: myapp.com -> myapp-alb-xxxx.elb.amazonaws.com

# Railway: Custom domain
railway domain add myapp.com

# Fly.io: Custom domain (note: dedicated IPv4 costs $2/mo)
fly certs add myapp.com
# Add CNAME: myapp.com -> myapp.fly.dev

# Verify DNS propagation
dig myapp.com

# After 48 hours of stable operation:
heroku apps:destroy myapp --confirm myapp
```

## Code Examples

### Terraform: AWS ECS Fargate infrastructure for Heroku migration

> Full script: [terraform-aws-ecs-fargate-infrastructure-for-herok.txt](scripts/terraform-aws-ecs-fargate-infrastructure-for-herok.txt) (59 lines)

```hcl
# Input:  Docker image in ECR, VPC/subnet IDs
# Output: Complete ECS Fargate service with ALB, auto-scaling
resource "aws_ecs_cluster" "main" {
  name = "myapp-cluster"
}
# ... (see full script)
```

### Bash: Complete Heroku-to-Railway migration script

> Full script: [bash-complete-heroku-to-railway-migration-script.sh](scripts/bash-complete-heroku-to-railway-migration-script.sh) (32 lines)

```bash
#!/bin/bash
# Input:  Heroku app name, Railway project
# Output: Fully migrated app on Railway
set -euo pipefail
HEROKU_APP="$1"
# ... (see full script)
```

### GitHub Actions: CI/CD for ECS Fargate deployment

```yaml
# 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
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: us-east-1
      - uses: aws-actions/amazon-ecr-login@v2
      - run: |
          docker build -t $ECR_REGISTRY/myapp:${{ github.sha }} .
          docker push $ECR_REGISTRY/myapp:${{ github.sha }}
      - uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: task-def.json
          service: myapp-service
          cluster: myapp-cluster
```

## Anti-Patterns

### Wrong: Storing files on the local filesystem

```javascript
// ❌ BAD -- Heroku's ephemeral filesystem, same on ECS/Railway/Fly.io
const path = require('path');
app.post('/upload', (req, res) => {
  const filePath = path.join(__dirname, 'uploads', req.file.filename);
  fs.writeFileSync(filePath, req.file.buffer);
  // File disappears on next deploy or container restart!
});
```

### Correct: Use object storage (S3, R2, etc.)

```javascript
// ✅ GOOD -- Use S3 or compatible object storage [src1, src2]
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({ region: process.env.AWS_REGION });

app.post('/upload', async (req, res) => {
  await s3.send(new PutObjectCommand({
    Bucket: process.env.S3_BUCKET,
    Key: `uploads/${req.file.filename}`,
    Body: req.file.buffer,
  }));
  // File persists across deploys and container restarts
});
```

### Wrong: Hardcoding Heroku-specific environment patterns

```bash
# ❌ BAD -- Relying on Heroku-specific env vars or add-on URLs
DATABASE_URL=$(heroku config:get DATABASE_URL)  # Won't exist on new platform
REDIS_URL="$HEROKU_REDIS_URL"  # Heroku-managed variable
```

### Correct: Use platform-agnostic configuration

```bash
# ✅ GOOD -- Use standard env vars, set per platform [src1, src3]
# AWS: SSM Parameter Store or Secrets Manager
DATABASE_URL=$(aws ssm get-parameter --name /myapp/database-url --with-decryption --query Parameter.Value --output text)
# Railway: automatically injects DATABASE_URL for linked plugin
# Fly.io: fly secrets set DATABASE_URL=postgres://...
```

### Wrong: Using pg_restore without --no-owner

```bash
# ❌ BAD -- Heroku roles don't exist on target
pg_restore -h rds-host -U postgres -d myapp heroku_backup.dump
# ERROR: role "uf3kxyz" does not exist
```

### Correct: Strip ownership during restore

```bash
# ✅ GOOD -- Ignore Heroku-specific roles [src2, src5]
pg_restore -h rds-host -U postgres -d myapp --no-owner --no-acl heroku_backup.dump
```

## Common Pitfalls

- **Forgetting worker processes**: Heroku's Procfile can define `web`, `worker`, `clock` processes. On AWS, each needs a separate ECS service/task. On Railway, separate services. On Fly.io, separate process groups in `fly.toml`. [src1]
- **Database connection limits**: Heroku Postgres has built-in connection pooling (PgBouncer). AWS RDS does not by default -- add RDS Proxy or PgBouncer yourself. Railway includes pooling. [src2, src5]
- **SSL certificate management**: Heroku provides free SSL. AWS requires ACM certificates + ALB configuration. Railway and Fly.io provide automatic SSL via Let's Encrypt. [src1]
- **Build vs. runtime environment**: Heroku buildpacks handle both build and runtime. With Docker, ensure build dependencies aren't in the final image (use multi-stage builds). [src5]
- **Scheduled jobs**: Heroku Scheduler -> AWS EventBridge + Lambda or scheduled ECS tasks; Railway cron services; Fly.io scheduled Machines. Don't use `cron` inside containers. [src1, src3]
- **Log aggregation**: Heroku provides `heroku logs --tail`. AWS needs CloudWatch Logs configuration. Railway has built-in logs. Fly.io has `fly logs`. Set up log forwarding early. [src2]
- **Fly.io IPv4 billing**: Dedicated public IPv4 addresses on Fly.io cost $2/mo per app. Use shared IPv4 (included free) or IPv6 if possible. [src4]
- **Railway egress costs**: As of March 2025, Railway reduced egress pricing, but high-bandwidth apps should monitor usage against included credits. [src8]

## Diagnostic Commands

```bash
# Heroku: capture current state before migration
heroku ps -a myapp                          # Current dyno formation
heroku config -a myapp --shell              # All config vars
heroku addons -a myapp                      # All add-ons
heroku pg:info -a myapp                     # Database details
heroku logs --tail -a myapp                 # Recent logs

# AWS ECS: verify post-migration
aws ecs describe-services --cluster myapp-cluster --services myapp-service
aws ecs list-tasks --cluster myapp-cluster
aws logs tail /ecs/myapp --follow
aws ecs describe-tasks --cluster myapp-cluster --tasks <task-id>  # Check health

# Railway: verify post-migration
railway status
railway logs
railway variables
railway domain list

# Fly.io: verify post-migration
fly status
fly logs
fly secrets list
fly postgres connect -a myapp-db
fly checks list  # Verify health checks
```

## 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 ($5 credit), Pro $20/seat/mo ($20 credit) |
| Fly.io | Edge deployment, global distribution | Dynos + multi-region | Trial only + pay-per-Machine-second (CPU/RAM presets + $5/30 days per extra GB RAM) |
| AWS Elastic Beanstalk | Managed platform, Heroku-like | Direct alternative | EC2 instance pricing |
| 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 |
|---|---|---|
| Heroku costs exceed $500/mo | App is simple and under $50/mo | Stay on Heroku Eco |
| Need auto-scaling beyond Heroku limits | Team has no DevOps experience | Railway (Heroku-like) |
| Compliance requires specific cloud (AWS/GCP) | Timeline is < 2 weeks | Plan longer migration |
| Need multi-region deployment | Single-region, low traffic | Any platform works |
| Need VPC networking or private subnets | Running stable on Heroku | Only migrate if costs justify |
| Team outgrew Heroku's operational ceiling | App is in prototype/MVP phase | Stay on Heroku or Railway |

## Important Caveats

- **Heroku entered "sustaining engineering" mode on 2026-02-06.** Salesforce announced no new features and no new Enterprise contracts for new customers — engineering investment is shifting to Salesforce AI initiatives. Existing customers can continue and renew with no pricing/service changes, but the platform is no longer evolving. Teams planning infra for 2-5 years should factor this in. [src9]
- Heroku's free tier was removed in November 2022 -- the Eco plan ($5/mo) replaced it. Fly.io also deprecated its Hobby/Launch/Scale free allowances in 2024; new signups now get a 2-VM-hour / 7-day trial before requiring a credit card. Railway's Hobby plan ($5/mo with $5 credit) remains the closest free-ish entry point. [src4, src8]
- AWS ECS Fargate has higher initial complexity than Heroku but significantly lower per-unit compute costs at scale (often 50-70% cheaper for production workloads). [src2, src6]
- Railway's auto-scaling is vertical (more CPU/RAM per instance) -- for horizontal scaling, you manually add replicas. Railway also offers committed spend tiers starting at $10,000/mo for dedicated hosts. [src3, src8]
- Fly.io deploys to specific regions; ensure your database region matches your primary compute region to avoid latency. Volume snapshots became billable in January 2026 ($0.08/GB/mo, first 10GB free). [src4]
- All platforms require explicit file storage migration -- move from Heroku's ephemeral filesystem to S3, R2, or equivalent.
- AWS Fargate now supports custom container stop signals (SIGQUIT, SIGINT) -- configure graceful shutdown for apps that don't use SIGTERM. [src7]

## Related Units

- [Docker Compose to Kubernetes Migration](/software/migrations/docker-compose-to-kubernetes/2026)
- [Monolith to Microservices Migration](/software/migrations/monolith-to-microservices/2026)
- [Jenkins to GitHub Actions Migration](/software/migrations/jenkins-to-github-actions/2026)
