Heroku To Aws

Heroku To Aws

Reading time1 min
#Cloud#AWS#Migration#Heroku#DevOps

Streamlining Your Migration: How to Efficiently Move from Heroku to AWS with Minimal Downtime

Heroku enables rapid prototyping and quick launches with clean abstractions. But eventually, teams hit scaling limitations, unpredictable billing, and restricted configuration. AWS—though more complex—offers fine-grained control, cost optimization, and architectural flexibility. The trade-off: orchestration overhead and a steeper learning curve.

Faced with needing to run a Rails app at scale—100GB+ Postgres, Redis caching, some nightly Sidekiq batch jobs—I encountered all the usual Heroku sticking points: noisy neighbor resource limits, the inability to tweak instance internals, and a growing monthly bill. Migration to AWS followed, with emphasis on minimal downtime and data integrity. Here’s what actually worked.


1. Audit and Blueprint Existing Heroku Workloads

Start with facts, not guesses.

  • Enumerate all dynos, buildpacks, add-ons, and OAuth integrations. Capture versions and billing plans.
  • Pull environment with
    heroku config -a your-app > .env.current
    
  • Log Heroku Postgres size and active connections.
    heroku pg:info -a your-app
    
  • List all scheduled tasks (review Heroku Scheduler, review any Procfile definitions).

Side note: Add-ons like Papertrail or external SMTP services rarely migrate cleanly. Expect to re-architect these on AWS.


2. Map Each Component to AWS Equivalents

Abstraction vanishes. You’re now responsible for networking, IAM, and deployment patterns.

Typical mappings:

Heroku ComponentAWS Analog
Web dynosECS/Fargate, EKS, or EC2
Heroku PostgresAmazon RDS (Postgres)
Heroku RedisElastiCache Redis
Scheduler/WorkersECS Scheduled Task, Lambda, or EC2 Cron
ENV/config varsSSM Parameter Store, Secrets Manager
  • Draw a component diagram. If the app is containerized (Dockerized on Heroku CI), ECS is a natural fit. Otherwise, EC2 (with systemd for process management).
  • Networking: design a VPC with public/private subnets, at least two AZs. Security Groups ≠ Heroku’s ephemeral routing—plan layer 7/4 firewalling and IAM roles tightly.
  • Load balancing: ALB covers HTTP(S); NLB if raw TCP is needed.

Tip: Start IaC from day one using Terraform (>=1.0 recommended) or AWS CloudFormation. Retrofit is painful.


3. Stand Up a Parallel Environment

No big-bang cutovers. Bring up a full AWS stack while Heroku stays live.

  • Provision RDS with multi-AZ and backups enabled.
  • Build/deploy containers to ECS using ECR as registry (or deploy code to EC2 directly for non-containerized workloads).
  • Use heroku pg:backups:capture → fetch to local, then pg_restore into RDS:
    heroku pg:backups:download -a your-app
    pg_restore --no-owner -d postgresql://user:password@aws-rds-endpoint/mydb latest.dump
    
    (Note: indexes and extensions sometimes require manual reconciliation.)
  • Move ENV variables/secrets:
    aws ssm put-parameter --name "/myapp/DB_URL" --value "postgres://..." --type "SecureString"
    
  • Install a metrics/monitoring agent early (Datadog, Prometheus sidecar, or CloudWatch agent).

Known issue: Out-of-order migration of environment variables leads to silent auth failures. Triple-check deployments.


4. Data Migration with Ongoing Sync

Databases rarely freeze for hours without consequence. Opt for staged migration.

  • Initial bulk data dump brings up main tables.
  • Set up logical replication (pglogical, AWS DMS) before user traffic resumes. This enables ongoing changes to flow from Heroku Postgres → AWS RDS.
  • For Redis, use redis-cli --rdb to export a snapshot, but expect cache misses post-migrate.
  • Large tables (>10M rows): Parallelize imports, disable triggers as needed, re-enable constraints after.

Practical error:

ERROR:  extension "pg_stat_statements" already exists

Often encountered during restore; safe to DROP EXTENSION ... before re-playing schema, if extension was preinstalled.


5. Controlled Cutover: Traffic Shifting

  • Set DNS TTL to 60 seconds at least 24 hours before migration.
  • Smoke-test on AWS with a subset of internal or beta users. Dark launching behind a feature flag is effective.
  • Blue/green deployment: keep Heroku as blue, AWS as green. Validate logs and error rates as DNS change propagates.
  • Watch for sticky sessions; ALB by default is stateless unless configured otherwise.
  • Gotcha: If you use Cloudflare or a CDN, cache may mask real AWS-origin failures for up to an hour post-switch.

Sample Route 53 weighted policy for 10% test traffic:

- Name: api.mydomain.com
  Type: A
  RoutingPolicy: Weighted
  Records:
    - Value: 18.223.17.4
      Weight: 90   # Heroku
    - Value: 3.45.183.76
      Weight: 10   # AWS
  TTL: 60

6. Finalize, Monitor, Decommission

  • Final incremental DB/Redis sync
  • Toggle production DNS
  • Monitor in real time. Expect up to 5% of users to be briefly impacted, especially during DNS propagation or if clients ignore TTL
  • Keep Heroku add-ons running (with billing minimized) for an agreed fallback window (typically 72h)
  • Migrate/no-op any remaining ephemeral storage or attached services (e.g., S3 assets)

After one week of stable AWS operations, terminate Heroku dynos and add-ons. Update runbooks and incident response docs.


Additional Lived Experience

  • IAM role misconfigurations caused most initial outages, not application bugs.
  • VPC endpoints for RDS and S3 generally outperform legacy NAT approaches.
  • Automated smoke tests (e.g., Cypress, or curl against key endpoints from multiple regions) catch regressions earlier than manual checks.

Non-obvious: Heroku’s log drain format doesn’t match CloudWatch. Custom parsers may be necessary if you depend on log-based metrics.


Migrating from Heroku to AWS requires careful inventory, meticulous data movement, and staged go-live validation. Avoid “lift and shift” dogma; smooth transitions are built on parallelism and small, testable changes. The result: a predictable, scalable infrastructure—at the cost of greater operational responsibility.

Questions about the process, or experiences with specific tools like DMS or ECS? Happy to share Terraform modules or migration playbooks that have worked in production.