Skip to main content

Two PaaS Freezes, One Exit Plan: Where App Runner and Heroku Workloads Go Next

11 min readDora NodaDora Noda
Share
On this page

On February 6, 2026, Heroku told its customers the roadmap was over. On April 30, 2026, AWS closed App Runner to new customers. Nobody's app went down on either date — and that is exactly why both events are dangerous. A shutdown gives you a deadline and a runbook. A freeze gives you permission to do nothing, right up until the month doing nothing stops working.

The two freezes landed twelve weeks apart, and they strand different workloads in different ways. This post puts them side by side: what exactly froze on each platform, a workload-by-workload map of where to land, and the ordered exit-planning checklist to work through — whether you migrate this quarter or the quarter your platform announces its own sustaining mode. For the full backstory on each freeze, see our deep dives on App Runner's closure and Heroku's freeze six months in; this post is the action layer on top of both.

Both doors are closed. Neither app went down.​

The freezes rhyme, but they are not the same freeze. The asymmetry matters because it sets your clock:

Heroku (Feb 6, 2026)App Runner (Apr 30, 2026)
Announcement"An Update on Heroku" — sustaining-engineering modelAvailability change — closed to new customers
New customersSelf-serve still open; no new Enterprise contractsNone, full stop
New featuresNone planned; stability, security, reliability onlyNone planned; security and availability only
Existing workloadsKeep running and deployingKeep running and deploying — existing accounts can still create new services
Official next stepNone named (see where teams landed)ECS Express Mode

Two details people get wrong. First, App Runner's freeze is the gentler of the two on paper: an existing AWS account can still spin up new App Runner services today, while Heroku has slammed the enterprise door and told its largest customers there is no new contract to sign. Second, Heroku's freeze sits on top of a second, older clock: the Heroku-20 stack went end-of-life on April 30, 2025 — no security updates, no new builds until you upgrade — with Heroku-22 supported through April 2027 and Heroku-24 (the default) through April 2029. If you are frozen on both axes, fix the build portability first; our pack build playbook is the guide for that half.

Where each workload lands: the decision matrix​

Start here, not with a vendor bake-off. Name your workload shape, read across, and you have a landing zone plus the reason it wins. The sections below fill in the migration mechanics behind each row.

Your workloadLand hereWhy
Public container service already on AWSECS Express ModeAWS's named successor: one call provisions Fargate plus load balancing, with CloudFormation/CDK/Terraform support and no Express surcharge beyond the underlying resources
Private service reaching RDS over a VPC connectorECS Express Mode in your own VPCThe connector hack becomes real networking — subnets and security groups you control instead of a bolt-on
Dyno plus Procfile web/worker, buildpack-builtRenderThe closest Heroku-shaped replacement: native runtimes, private services, and a render.yaml for what your Procfile and add-ons used to declare
Small project or preview-heavy teamRailwayCheapest path for small services, unlimited seats, and PR deploys that replace review apps with the least ceremony
Latency-sensitive or multi-region service, Docker-comfortable teamFly.ioReal global placement and Machines-level control — at the cost of owning Dockerfiles and region math Heroku never asked you to do
Stateful core: Postgres, Redis, object storageDecouple data from compute firstMove state to a managed data service (provider-native, Neon, Upstash, or RDS) before you move compute; compute is portable, data gravity is not
Review apps and pipelinesRender preview environments or Railway PR deploysBoth reproduce the per-PR ephemeral environment; budget a day to re-express pipeline promotion as branch-based deploy rules
Salesforce-synced app on Heroku ConnectStay on Heroku for nowThere is no good 1:1 Connect replacement — this workload's migration is a sync re-platform, not a hosting move, and deserves its own project
A fleet, not an app: many services, cost-driven, or agent-operatedSelf-hosted PaaS on machines you ownSingle-box tools (Coolify, Dokploy) for one server; a Cluster API fleet when you need declarative multi-machine lifecycle — and raw Kubernetes only if you already operate it

One budget line before you start: Express Mode's Fargate compute runs in the same range App Runner billed (about $0.064 per vCPU-hour and $0.007 per GB-hour), but it provisions a standalone Application Load Balancer at roughly $16-plus per month even idle — a baseline App Runner bundled into its managed price. At low traffic, the AWS-blessed path costs more than the frozen service it replaces. Price that before you migrate, not after; our App Runner economics section works through the math.

What each retreat actually strands​

The matrix tells you where to go. This section tells you what does not survive the trip 1:1 — the inventory behind each row.

Stranded on App Runner​

App Runner's whole pitch was the absence of AWS plumbing: no VPC, no cluster, no task definition. Every one of those absences becomes a migration item:

  • GitHub auto-deploy wiring. Pushing to a branch and watching App Runner rebuild has no counterpart in Express Mode — plan for a real pipeline (CodePipeline, GitHub Actions to ECR/ECS) from day one.
  • apprunner.yaml build configs. The repo-side build file does not transfer. Your Express Mode service wants a container image, so the build step moves into CI or ECR with its own Dockerfile or buildspec.
  • VPC connectors. Private RDS/EC2 access via a connector becomes genuine VPC design: subnets, routing, and security groups. Better end state, real work to get there.
  • Custom domains and certificates. Domain associations must be removed from App Runner and recreated on the new service; AWS's own migration guide walks a Route 53 weighted-record shift for exactly this reason.
  • Scaling and role config. Concurrency-based autoscaling and instance roles get re-expressed as Fargate service autoscaling plus task execution and infrastructure roles.

Stranded on Heroku​

Heroku's pitch was the absence of infrastructure thinking at all: a Procfile, a buildpack, and an add-on marketplace. Each absence strands something different:

  • Procfiles and dyno formation. Web/worker process types map to start commands and service definitions on every alternative — mechanical, but every process type needs an explicit owner on the new platform.
  • Classic buildpacks. If you have no Dockerfile, your build only exists inside Heroku's toolchain. Reproduce it portably with pack build against Heroku's own heroku/builder:24 before you move anything else.
  • Add-on Postgres and Redis. Heroku Postgres and Heroku Redis/Key-Value Store are the data gravity. Database moves lead the schedule (checklist item 3), and every other add-on — logging, monitoring, email — needs a portable replacement lined up.
  • Review apps and pipelines. The promotion-flow mental model (review to staging to production) has no universal equivalent; each target re-expresses it as branch rules plus ephemeral environments.
  • Scheduler. Cron-equivalent on every target (Railway Cron, Render Cron, scheduled Machines, or GitHub Actions cron) — small item, frequently forgotten until the first missed nightly job.
  • Heroku Connect. As the matrix says: no 1:1 replacement. Salesforce sync is the single strongest reason to stay, and leaving it means re-platforming synchronization itself.

Note what is not on this list: your application's code, which both freezes leave untouched, and your domain names, which transfer anywhere. Everything stranded is platform-shaped glue — which is also the argument for preferring portable glue (Dockerfiles, standard Postgres, external CI) on whatever you land on next.

The exit-planning checklist, in order​

Work this list top to bottom. Each step's output feeds the next, and the order is deliberate: inventory before builds, builds before data, data before cutover.

1. Inventory everything the platform knows about you. Before touching a new provider, capture the full surface: build inputs, process types, config vars, add-ons, domains, preview environments, scheduled jobs, and private-network attachments.

bash
# Heroku: the five commands that capture your whole app shape
heroku stack --app myapp
heroku buildpacks --app myapp
cat Procfile
heroku config --app myapp --shell | cut -d= -f1
heroku addons --app myapp
bash
# App Runner: pull the service config AWS has been holding for you
aws apprunner list-services
aws apprunner describe-service --service-arn <arn>

Save this output. It becomes the parity checklist you verify against after every later step.

2. Make the build portable. A build that only runs on the frozen platform is a migration blocker wearing a deployment pipeline as a disguise. Heroku teams: pack build your slug into an OCI image with Heroku's own builder and run it locally until it matches production. App Runner teams: your ECR-image services are already portable — promote the image build into CI; your GitHub-source services need a Dockerfile or buildspec that builds outside App Runner. Do not skip to data migration with an unportable build; every rollback during the later steps depends on being able to rebuild.

3. Move data first, while both platforms exist. Databases lead the schedule because they carry the only state you cannot recreate. Stand up the new Postgres/Redis, replicate or dump-and-restore into it, and run the new copy as a follower/shadow for a full business cycle before anything reads from it in production. For Heroku Postgres specifically, decide up front between your target's managed Postgres and an external provider — the choice constrains connection strings, extensions, and follower options for everything downstream.

4. Replace private networking deliberately. App Runner VPC-connector users: design the target VPC (or the target platform's private-network equivalent) before cutover week, and verify private reachability from the new compute to the new data stores with the old path still live. Heroku Private Space users: private services plus allow-listed data ingress on the target. This is the step most often discovered mid-cutover; discovering it in week one is the whole point of the list.

5. Rebuild preview environments and CI before you need them. The week you cut over production is the worst week to discover your PR-preview flow does not exist on the new platform. Stand up branch deploys, ephemeral review environments, and the CI pipeline that builds your now-portable image while production still runs on the old platform — then exercise them with a real feature branch, not a hello-world push.

6. Cut over with the old platform as your rollback. Shift traffic gradually (Route 53 weighted records on AWS; low-TTL DNS plus dual deploy everywhere else), validate against the parity checklist from step 1, and keep the frozen service warm as your instant rollback — AWS's own guidance keeps App Runner live until the validation period passes; give Heroku the same 48-hour courtesy. Only then remove domain associations from the old service and decommission. "Decommission" includes the add-ons and the connector/VPC leftovers, which bill quietly forever if you forget them.

When staying frozen is the rational choice​

Exit planning is not exit panic. Staying is defensible when all three of these hold: your workload is on the stay-list (Salesforce-synced apps, low-churn internal tools with a known, tolerable bill), your secondary clocks are green (Heroku-24 stack with runway to 2029, no EOL buildpack in your chain, no App Runner dependency on a region or feature you do not already have), and you have written down the trigger that reopens the question — a renewal date, a stack EOL, a feature you need that will never ship.

Frozen limits are now permanent properties, so "we'll migrate when it hurts" needs the hurt defined in advance: the 30-second timeout, the daily restart, and the two-region map are not getting fixed.

The deeper lesson of two freezes in twelve weeks is about the layer, not the vendors. Both companies decided the git-push convenience layer was not worth extending — AWS on infrastructure it fully owns, Salesforce on a platform it acquired in 2010. Any team still renting that layer should have the checklist above filled in before their provider makes the same announcement, because the next sustaining-mode memo will not come with a migration runbook either.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. When the managed layer freezes, owning the layer is the exit plan. Star the repo on GitHub or deploy your first app today.

Related articles

Check your move before you migrate

Free browser tools: check a render.yaml or your Render scripts against bex, or turn a Heroku app or docker-compose.yml into a draft render.yaml. Nothing you paste leaves your browser.

Open the migration tools