Heroku to AWS Migration: Step-by-Step Plan and Costs
DevOps Engineering

Heroku to AWS Migration: Step-by-Step Plan and Costs

When to leave Heroku, which AWS target to pick, real costs, moving Postgres to RDS, and a cutover runbook.

Michael Zion

0 min read

A Heroku to AWS migration usually means moving your dynos to containers on Amazon ECS with Fargate, Heroku Postgres to Amazon RDS, and Heroku Key-Value Store to ElastiCache. Config vars move to Secrets Manager, and you cut over by DNS. Plan it in phases: inventory, AWS foundations, containers and CI/CD, a rehearsed database move, then a short maintenance window.

This guide is the plan we would hand a startup CTO. It covers when leaving Heroku is worth it, which AWS target to choose, how every Heroku feature maps to AWS, a real cost comparison, the Postgres migration in detail, a cutover runbook, and the gotchas that surface after launch. Prices are as of October 2026, with links to the sources.

Why teams are moving off Heroku in 2026

On February 6, 2026, Heroku announced it is "transitioning to a sustaining engineering model focused on stability, security, reliability, and support" (Heroku blog). Heroku still calls itself "an actively supported, production-ready platform," and no shutdown date has been announced. Coverage of the announcement reported that new feature development has stopped and that new Enterprise contracts are no longer sold to new customers, while existing contracts and card-billed accounts continue unchanged.

That doesn't mean you have to leave this quarter. It does mean you should stop assuming Heroku will grow with you. The concrete reasons teams give for migrating are usually these:

  • Cost at scale. Heroku is good value for small apps and expensive for memory-heavy ones. A Performance-L dyno with 14 GB of RAM is $500 a month. The cost comparison below shows the AWS equivalent.
  • The 30-second router timeout. Heroku's router ends any request that doesn't start responding within 30 seconds and logs an H12 error. Long uploads, reports and slow endpoints have to be redesigned around it.
  • Networking and compliance. Private networking, fixed outbound IP addresses, fine-grained IAM and audit requirements are either unavailable on the Common Runtime or require Private or Shield Spaces at a much higher price.
  • Everything else is already on AWS. Data pipelines, S3, queues and machine learning workloads end up in AWS anyway, and splitting the stack across providers adds latency and work.

Should you leave Heroku at all?

Migrating trades a simple operating model for control. If nothing on the right-hand side of this table applies to you yet, the cheapest move is often to stay and revisit in six months.

Stay on Heroku ifPlan the move if
One web process, a worker, Postgres and Redis cover your app.You run several services that need different scaling rules, private networking or service-to-service traffic.
Your Heroku bill is small compared with an engineer's time.The bill is forcing bad decisions, such as avoiding workers or squeezing memory to stay on a smaller dyno.
Nobody on the team has time to own infrastructure and on-call.You have, or can hire or contract, someone to own AWS, CI/CD and incidents.
You are still searching for product-market fit and change the product weekly.Enterprise customers ask for security reviews, data residency, private connectivity or audit evidence.
Uptime and recovery are acceptable to your customers today.Incidents are hard to diagnose because logs, metrics and deploy history are scattered across add-ons.

Be specific before you decide. "We need to scale" is not a reason. "Background jobs back up every evening because we can't scale workers independently of web" is. If the real bottleneck is the database, query tuning and caching will help more than changing platforms.

Choosing your AWS target

The AWS target decides how much of Heroku's convenience you rebuild yourself. For most teams leaving Heroku, Amazon ECS on Fargate is the right default: you run containers without managing servers, and it is far simpler to operate than Kubernetes.

OptionWhat you manageGood forWatch out for
ECS Express ModeA container image and two IAM roles. AWS creates the Fargate service, load balancer, scaling and networking.The closest thing to Heroku's simplicity for a single web service.Load balancer settings can't be changed on Express Mode services. You will outgrow it as networking needs grow.
ECS on Fargate (defined in Terraform)Task definitions, services, an Application Load Balancer, VPC, IAM and autoscaling, all as code.Most startups with a web app, workers and scheduled jobs. Our default recommendation.You own the VPC and networking design. Get it right once, in code.
Elastic BeanstalkAn application package, with environment settings.Simple monoliths whose teams want to avoid containers.Less flexible, and teams often migrate off it later.
Amazon EKS (Kubernetes)A Kubernetes cluster, add-ons, upgrades, ingress and everything on top.Many services, platform teams, or existing Kubernetes skills.The most operational work by far. A bad first step for a team coming from Heroku without Kubernetes experience.
AWS App RunnerAlmost nothing.Existing App Runner customers only.AWS closed App Runner to new customers on April 30, 2026 and recommends ECS Express Mode instead (AWS docs).
Spectrum of AWS targets from less to more to manage: ECS Express Mode, ECS on Fargate in Terraform marked as the default, Elastic Beanstalk and Amazon EKS, with AWS App Runner crossed out as closed to new customers.
ECS on Fargate in Terraform sits near the simple end while still covering web, workers and scheduled jobs.

If you are weighing Kubernetes anyway, read our comparison of Kubernetes hosting options for production and the Kubernetes migration guide first. Most teams leaving Heroku should start on ECS and move to Kubernetes later only if a specific need appears.

How Heroku features map to AWS

Heroku bundles many features you never think about. List all of them before you plan, because each one needs a replacement on AWS.

HerokuAWS equivalent (ECS on Fargate)Notes
Dynos and Procfile process types (web, worker)One ECS service per process type, sharing one image with different commandsSize each service separately. Workers often need more memory and less CPU than web.
Buildpacks and slug compilationA Dockerfile, or Cloud Native Buildpacks with pack build --builder heroku/builder:24, pushed to Amazon ECRHeroku's own builder image lets you containerize with almost no code changes.
Release phase (release: in the Procfile)A one-off ECS task (aws ecs run-task) in the pipeline, before the service updateUsually database migrations. Fail the deploy if the task fails.
Config varsAWS Secrets Manager or SSM Parameter Store, injected through the task definition's secretsSplit real secrets from plain configuration.
Heroku PostgresAmazon RDS for PostgreSQL or Aurora PostgreSQLMatch the major version and extensions. See the Postgres section below.
Heroku Key-Value Store (Redis)Amazon ElastiCache (Valkey or Redis OSS)Runs inside your VPC, so the app needs network access to it.
Heroku router and SSLApplication Load Balancer with an AWS Certificate Manager (ACM) certificateThe ALB's idle timeout defaults to 60 seconds. There is no 30-second cutoff.
Heroku SchedulerAmazon EventBridge Scheduler running ECS tasksYou get retries and monitoring that Scheduler never had.
heroku run one-off dynosaws ecs run-task, or ECS Exec into a running taskWrap the common ones (console, migrations) in scripts.
Log drains and logging add-onsAmazon CloudWatch Logs, or your existing vendor through FireLensSet retention policies, because CloudWatch keeps logs forever by default.
Pipelines, review apps, promotionGitHub Actions or another CI with OIDC to AWS. Preview environments are built separately.Review apps are the feature teams miss most. Budget time for them.
Static outbound IP add-onsA NAT gateway with an Elastic IPPartners that allowlist your IP need the new address before cutover.
Dyno autoscalingECS Service Auto Scaling on CPU, memory or ALB requests per targetSet minimums so a quiet night doesn't scale you to one task.

What it really costs: Heroku vs AWS

Compute prices for comparable capacity, using Heroku's pricing page and Fargate on-demand pricing for Linux x86 in US East (N. Virginia): $0.0404784 per vCPU-hour and $0.004446 per GB-hour, over 730 hours a month.

CapacityHerokuFargate (x86, on-demand)
1 GB of memoryStandard-2X: $50 a month0.5 vCPU and 1 GB: about $18 a month
2.5 GB of memory, dedicatedPerformance-M: $250 a month1 vCPU and 3 GB: about $39 a month
14 GB of memory, dedicatedPerformance-L: $500 a month2 vCPU and 14 GB: about $105 a month

Compute is the headline, but AWS adds costs that Heroku bundles into its price. Include them, or the business case will be wrong:

  • Application Load Balancer: roughly $16 a month per load balancer, plus usage.
  • NAT gateway: roughly $33 a month each, plus a per-GB processing charge. Use one per availability zone for high availability, or one in total to save money while you're small.
  • Public IPv4 addresses: $0.005 an hour each, which adds up when tasks have public IPs.
  • CloudWatch Logs: a per-GB ingestion charge. Chatty debug logging can cost more than the containers.
  • Databases: compare your Heroku Postgres plan with an RDS instance of the same memory and storage. A Multi-AZ deployment roughly doubles the instance cost and is what you want for production.
  • Engineering time: the migration itself and ongoing ownership of upgrades, patching, on-call and cost hygiene.

The pattern we see: a small app on Standard dynos saves little or nothing by moving, because fixed AWS costs (load balancer, NAT, Multi-AZ database) eat the difference. Savings become real once you would otherwise need Performance or Private dynos. Graviton (ARM) tasks are about 20% cheaper than x86 for the same size, and Savings Plans cut steady-state compute further. Our guide to reducing cloud costs without slowing engineering covers the guardrails that keep the AWS bill from creeping up after the move.

Bar chart of monthly compute cost: 1 GB is $50 on Heroku vs about $18 on Fargate, 2.5 GB dedicated is $250 vs about $39, 14 GB dedicated is $500 vs about $105, beside a list of AWS costs Heroku bundles in: load balancer, NAT gateway, public IPv4, CloudWatch Logs and a Multi-AZ database.
Containers are far cheaper than dynos, but for small apps the added AWS costs eat most of the difference.

The migration plan, step by step

Timeline of seven phases: inventory, AWS foundations, containers and CI/CD, staging on AWS for at least a week, rehearsing the database move at least twice, a short maintenance window cutover, and decommissioning Heroku after 2 to 4 weeks.
Almost all the work happens before cutover night, which is why the window itself can stay short.

Phase 1: Inventory everything Heroku runs

Capture the current state from the CLI so nothing is missed:

heroku apps
heroku ps --app my-app                 # process types, sizes and counts
heroku addons --app my-app             # every add-on and plan
heroku config --app my-app --shell     # config vars (store this file securely)
heroku pg:info --app my-app            # Postgres version, size, connections
heroku domains --app my-app            # custom domains and DNS targets
heroku releases --app my-app -n 20     # how often you deploy

For each add-on, decide whether to replace it with an AWS service, keep the vendor and connect directly (most monitoring and email vendors work fine from AWS), or drop it. Note every external system that allowlists your IPs or holds webhook URLs.

Phase 2: Build the AWS foundations in code

Create the accounts, VPC, subnets, IAM roles, ECR repositories, the ECS cluster, RDS, ElastiCache and Secrets Manager entries with Terraform from day one. A typical layout is public subnets for the load balancer and private subnets for tasks and databases, across at least two availability zones. Clicking through the console is fast in week one and expensive by month three. Our Terraform structure guide covers how to lay out the code so staging and production stay identical.

Phase 3: Containerize and rebuild CI/CD

If you don't have a Dockerfile, Heroku's Cloud Native Buildpacks builder produces an image from the same source Heroku builds today:

pack build my-app --builder heroku/builder:24
docker run --rm -e PORT=8080 -p 8080:8080 my-app

Heroku injects PORT at runtime. On ECS you choose a fixed container port in the task definition, so set PORT explicitly. Then build the pipeline: build the image once, push to ECR, run migrations as a one-off task, and update the ECS services. Use OIDC from your CI provider to AWS instead of long-lived access keys.

Phase 4: Run a full staging environment on AWS

Deploy staging on AWS with a recent copy of production data, and run it for at least a week. This is where you find the hidden dependencies: a webhook that only works from Heroku's IP ranges, a cron job nobody remembered, a library that assumed the daily dyno restart.

Phase 5: Rehearse the database migration

Run the full data migration against a scratch RDS instance at least twice, and time it. The time from the start of the dump to the end of the restore is your real maintenance window. The next section has the details.

Phase 6: Cut over

Follow the runbook further down. Most of the risk is in the database, so most of the runbook is about it.

Phase 7: Decommission Heroku

Keep the Heroku apps scaled to zero, with a final database backup downloaded and stored in S3, for two to four weeks. Then remove the add-ons and the apps. Add-ons keep billing until you remove them.

Migrating Heroku Postgres to Amazon RDS

The database is the hard part. Heroku Postgres doesn't give customers superuser access, which rules out the usual near-zero-downtime tools. Before you plan around native logical replication or AWS Database Migration Service (DMS) change data capture, confirm with Heroku support what your plan actually allows. Most teams end up choosing one of these approaches:

ApproachDowntimeEffortUse when
Dump and restore in a maintenance windowDump time plus restore time. Minutes for small databases, longer for large ones.LowThe database is up to a few tens of GB and a short window is acceptable. This is most startups.
Dump and restore, then copy the deltaShorter window, because only recent rows move during the cutoverMediumLarge, mostly append-only tables with reliable updated_at columns.
Application dual writesClose to noneHighVery large databases, or when no maintenance window is acceptable. Requires application code changes and careful verification.
Chart of downtime against effort for moving Heroku Postgres to RDS: dump and restore has the most downtime and least effort and suits most startups, dump, restore and copy the delta sits in the middle, and application dual writes has close to no downtime but needs code changes. Superuser access is not available on Heroku Postgres.
Every step toward less downtime costs more engineering effort, and most startups stop at the first one.

The dump and restore path, using Heroku's documented commands:

# 1. Stop writes
heroku maintenance:on --app my-app
heroku ps:scale worker=0 --app my-app

# 2. Take and download a fresh backup
heroku pg:backups:capture --app my-app
heroku pg:backups:download --app my-app      # writes latest.dump

# 3. Restore into RDS
pg_restore --verbose --clean --if-exists --no-acl --no-owner \
  --jobs=4 \
  -h my-db.xxxxxxxx.us-east-1.rds.amazonaws.com -U app_admin -d app \
  latest.dump

Details that cause failed restores:

  • Version match. Create RDS with the same Postgres major version as Heroku (shown by heroku pg:info), and use a pg_restore client at least as new as the server. Upgrade majors later, as a separate step.
  • The heroku_ext schema. Heroku installs extensions into a schema called heroku_ext. If the restore complains about it, create it first with CREATE SCHEMA heroku_ext;, as Heroku's own import docs suggest, and check that every extension you use is supported on RDS.
  • Large databases. Heroku says its PG Backups tool is intended for databases up to about 20 GB under moderate load. Beyond that, use pg_dump in custom format directly against the database URL during the maintenance window, and restore with parallel jobs.
  • SSL. Newer RDS for PostgreSQL versions require SSL connections by default. Make sure your database client is configured for it before cutover day.
  • Connection limits. If you used PgBouncer on Heroku, plan for RDS Proxy or a PgBouncer container on AWS. Many Fargate tasks with large connection pools can exhaust a small RDS instance.

Cutover runbook

  1. One week before: lower the TTL on your DNS records to 60 seconds. Tell partners about new outbound IPs and update webhook URLs that point at Heroku hostnames.
  2. One day before: run a final rehearsal restore, then deploy the exact release you will cut over with to AWS against the rehearsal database and run your smoke tests.
  3. Start of window: turn on Heroku maintenance mode and scale workers and the scheduler to zero, so nothing writes.
  4. Move the data: capture, download and restore as shown above. Verify row counts on your largest and most important tables, and the latest IDs and timestamps.
  5. Start AWS: point the AWS services at RDS and ElastiCache, start web and workers, run the smoke tests through the load balancer's own hostname.
  6. Switch DNS: point your domain at the load balancer (an alias record in Route 53). Watch the error rates, latency and queue depth for at least an hour.
  7. Rollback rule: decide in advance what triggers a rollback and the deadline for it. Before AWS accepts writes, rollback is just switching DNS back and turning off Heroku maintenance mode. After AWS has accepted writes, rolling back means copying those writes back to Heroku, so set the decision point before you open traffic.
Cutover flow: maintenance mode on, move the data, start AWS and smoke test, then switch DNS and watch for an hour. A dashed line before the DNS switch marks when AWS accepts writes: before it, rollback is switching DNS back and turning maintenance off; after it, rollback means copying writes back to Heroku.
Rollback is cheap only until AWS accepts writes, so the decision point comes before traffic opens.

If you want to move compute before data, you can run the AWS services against Heroku Postgres for a short period and shift traffic gradually with Route 53 weighted records. Heroku's Common Runtime itself runs on AWS, but measure the latency from your AWS region to the database before you rely on that.

Gotchas that show up after the move

  • Hidden memory leaks. Heroku restarts every dyno at least once a day, which hides slow leaks. On ECS a task can run for weeks, so watch memory graphs in the first month.
  • Slow endpoints reappear. Without the 30-second cutoff, requests that Heroku used to kill now hold connections for up to the ALB's 60-second idle timeout. Add your own timeouts in the application.
  • Graceful shutdown. ECS sends SIGTERM and waits for the task's stop timeout before killing it. Make sure workers finish or release their jobs when they receive the signal.
  • Health checks. The load balancer needs a fast health endpoint that doesn't depend on the database. Otherwise a slow database makes ECS replace every task at once.
  • Scheduler drift. Heroku Scheduler jobs are easy to forget because they aren't in the Procfile. Check the Scheduler dashboard before you turn Heroku off.
  • Observability. Decide where logs, metrics and traces live before cutover. If you used a logging add-on, the simplest path is often to keep that vendor and ship to it from ECS. Our startup observability stack guide covers the options.

How long a Heroku to AWS migration takes

It depends on the number of apps, add-ons and the database size more than on code size. In our experience, a single application with Postgres, Redis and a worker takes a few weeks of focused work for one experienced engineer. Most of that time goes into infrastructure as code, CI/CD and rehearsing the data move, not into running the containers. Many apps, review apps, compliance requirements or a multi-terabyte database stretch that to a few months. The cutover itself should be a planned window of under an hour for most startups.

Frequently asked questions

Is Heroku shutting down?

No shutdown has been announced. In February 2026, Heroku moved to a sustaining engineering model focused on stability, security, reliability and support, and it says it remains production-ready. New features have stopped, so plan your roadmap as if the platform will stay as it is today.

Is Heroku part of AWS?

No. Heroku is owned by Salesforce. It runs on AWS infrastructure, but it is a separate platform with its own pricing, support and limits. Moving to AWS means running the services in your own AWS account.

What is the AWS equivalent of Heroku?

For new customers, AWS points to Amazon ECS Express Mode as the simplest way to run a container with a load balancer and scaling. For production apps with workers and scheduled jobs, ECS on Fargate managed with Terraform is the closest full replacement. AWS App Runner was the previous answer, but it closed to new customers on April 30, 2026.

Can I migrate Heroku Postgres to RDS with zero downtime?

Not easily. Heroku Postgres doesn't give customers superuser access, so the usual replication-based tools often can't connect. Most startups accept a maintenance window of minutes to an hour for a dump and restore. Truly zero-downtime moves need application-level dual writes or an approach confirmed with Heroku support.

What hidden costs come with moving a Node.js API from Heroku to Fargate?

The load balancer, NAT gateway, public IPv4 addresses, CloudWatch Logs ingestion and a Multi-AZ database. There is also the engineering time to own the platform afterwards. Containers alone are cheaper than dynos, but those fixed costs mean small apps save little.

Should we move to Kubernetes (EKS) when we leave Heroku?

Usually not as the first step. A team without Kubernetes experience takes on upgrades, ingress, add-ons and a lot of new failure modes all at once. Start on ECS with Fargate, and move to EKS later if you need what only Kubernetes offers.

Why is Heroku no longer free?

Heroku removed its free dynos and free databases in November 2022. The cheapest options now are Eco dynos at $5 a month and Essential-0 Postgres at $5 a month.

Getting help with the move

A Heroku exit is mostly a planning and rehearsal problem: the containers are the easy part. MeteorOps engineers run these migrations for startups, from the Terraform foundations to the cutover night. If you want a second opinion on your plan, book a free consultation.