Skip to main content

Heroku Is Frozen and Fir Can't Run Your Dockerfile: Which Workloads to Move First

12 min readDora NodaDora Noda
Share
On this page

On February 6, 2026, Salesforce moved Heroku into what it calls a sustaining engineering model: stability, security, reliability, and support — but no new features, and no new Enterprise Account contracts for new customers. Existing apps keep running and existing enterprise deals can renew, so nothing breaks on day one. The breakage is slower and more specific than that: Heroku's successor runtime, the Fir generation (generally available since April 2025), does not support Docker deploys at all. The Container Registry remains Cedar-only and amd64-only, and Heroku's own docs say it plainly: deploying Docker images is not supported on Fir-generation apps.

So here is the verdict up front, with the receipts below: if you run containerized workloads on Heroku, you are on a frozen platform with no on-platform successor — move the Docker workloads first. Buildpack apps can ride out sustaining-mode Cedar, and since September 2026 they can even adopt Cloud Native Buildpacks without leaving Cedar. Your Dockerfiles have no such bridge. Every quarter they sit on a frozen registry, the drift between what you run and what the rest of the ecosystem assumes gets wider, and the eventual move gets harder. Sequence the estate accordingly: containers first, buildpacks later, and land the containers on a target that takes your Dockerfile as-is.

The double bind, stated precisely

Two facts, each survivable alone, combine into a trap for exactly one workload class.

Fact one: the freeze. The February announcement, widely reported as Salesforce putting Heroku "out to PaaSture," ends the product's evolution. No new features means the Cedar generation you run today is the Cedar generation you will run indefinitely: the same Container Registry, the same amd64-only constraint, the same add-on surface. Credit-card billing continues unchanged, which makes the freeze easy to ignore — your deploys still go green, your invoices still arrive. That is precisely why it is dangerous for planning: a platform can be frozen for years while feeling fine, right up until the workload you need to run is one the frozen shape cannot express.

Fact two: Fir is not your successor if you ship containers. The Fir generation is a genuine modernization — dynos as OCI containers on Amazon EKS backed by Graviton (ARM) instances, Cloud Native Buildpacks instead of classic buildpacks, OpenTelemetry-native observability. But its feature-parity story has hard edges: no Docker deploys, no Shield spaces, no retained log history (Fir logs are a live stream — miss the event and it is gone). Teams that assumed "we'll migrate Cedar to Fir when the time comes" need to hear the actual answer: for containerized apps, there is no Fir migration path. Heroku is not saying "Docker support is coming to Fir"; it built a successor generation whose build story is CNB-only and left the registry on the frozen generation.

The bind: the only generation that runs your images is frozen, and the generation still evolving cannot take your images. That is the whole planning input. Everything below follows from it.

Inventory your estate in three buckets

Before sequencing anything, classify every Heroku app you own. Most estates sort cleanly into three buckets with very different futures:

BucketWhat it looks likeOn-platform future?Move urgency
Docker on Cedarheroku container:push, container stack, custom images in the registryNone — registry is Cedar-only on a frozen platformHighest — move first
Classic buildpack slugsgit push heroku main, Heroku-managed buildpacks, ProcfileSustained indefinitely on Cedar; CNB bridge available since Sept 2026Low — can stay for now
CNB-compatible appsStandard frameworks, no exotic build steps, portable to Paketo-style buildsBest of the three: runs on Cedar today, portable to any CNB platformLowest — move at leisure

Be honest about the first bucket's size. Docker-on-Heroku estates are usually bigger than teams remember, because the registry absorbed every workload the buildpacks could not express: the service with the vendored C dependency, the monolith with the multi-stage build, the worker pinned to a system library the Cedar stacks stopped shipping, the app someone containerized "temporarily" in 2023. Run heroku apps --all and check each app's stack — anything on the container stack is bucket one, no matter how small the app. Those are the workloads with no future on the platform, and they are your phase one.

A useful forcing function: for each bucket-one app, write down the last time its base image was rebuilt from scratch. Images that cannot be rebased — Heroku explicitly warns that Dockerfile-built images, unlike buildpack outputs, never get automatic OS patching — rot quietly. A two-year-old base image with known OpenSSL CVEs sitting on a frozen registry is not "stable infrastructure." It is deferred work compounding at security-advisory interest rates.

Move the Docker workloads first — the sequence

The sequencing argument is not "containers are more modern." It is colder than that: bucket one is the only bucket whose platform is both frozen and successor-less, and its artifact is the most portable one you own. A Dockerfile is already a platform-independent build contract. Moving it is replatforming the thinnest possible layer — the registry, the runtime, the routing — while the artifact stays byte-identical. Buildpack apps, by contrast, embed Heroku's build system in their deploy path; each one needs its build story re-solved at the destination. Do the portable, stranded workloads first and the entangled, sustained workloads later.

Concretely, phase one runs like this:

  1. Freeze the Dockerfile inventory. For each bucket-one app, pin the base image digest, record the build args and the registry URL, and confirm the image builds from a clean checkout. If it does not build reproducibly today, fix that before moving anything — a migration is the worst time to discover the build depends on a developer laptop.
  2. Extract the Heroku-isms around the image. Environment variables (heroku config), add-on connection strings (Postgres, Redis), custom domains and certs, and scheduler jobs. The Dockerfile moves as-is; everything around it gets re-created at the destination.
  3. Pick the landing target once, for the whole bucket. Do not evaluate platforms per app. Bucket one shares one property — it is container images — so it gets one decision: a Dockerfile-native target (more on the options below). One target, one runbook, N apps.
  4. Cut over behind DNS, lowest-risk app first. Ship the smallest bucket-one service to the new target, move its domain, watch it for a full deploy cycle, then work upward in blast radius. Keep the Heroku app stood down but undeleted for one billing cycle as instant rollback.
  5. Decommission the registry usage last. Only when the final bucket-one app has cut over do you stop paying for the container-shaped dynos. Until then, Heroku is your rollback environment — that is a legitimate use of a sustaining-mode platform.

Note what this sequence deliberately postpones: the great buildpack debate. Teams that start by arguing "buildpacks vs Dockerfiles vs Nixpacks at the destination" stall for months. Phase one has no such debate — the artifact already exists, the destination takes Dockerfiles, the only questions are mechanical. Save the philosophy for phase two, when the stranded workloads are safe and the clock pressure is off.

Where the Dockerfiles land

The destination decision has one hard requirement — it must take your existing Dockerfile without demanding a rewrite — and one strong preference: Render-compatible semantics, because Render's model (runtime: docker in render.yaml, build from your Dockerfile, private services on an internal network) is the closest thing the git-push world has to a portable contract for containerized apps. Two target shapes satisfy this:

Shape one: a Dockerfile-native managed PaaS. Render is the reference implementation — native Docker support, free tier for staging, paid plans from around 7permonth,privatenetworkingbetweenservices.RailwayandFly.iotakeDockerfilestoo.Themigrationmechanicsareuniform:pointthenewserviceattherepo,letitbuildtheDockerfile,recreateenvvarsandaddons,moveDNS.ForteamswhoseHerokubillisdominatedbyproductiondynos(realisticproductionfloorsrun7 per month, private networking between services. Railway and Fly.io take Dockerfiles too. The migration mechanics are uniform: point the new service at the repo, let it build the Dockerfile, re-create env vars and add-ons, move DNS. For teams whose Heroku bill is dominated by production dynos (realistic production floors run 85–$100 per month per app before add-ons), the managed-PaaS move is usually cost-neutral or cheaper — but cost is not the point of phase one. Optionality is: every Dockerfile that leaves the Cedar registry is a workload that can move again, anywhere, with the same artifact.

Shape two: a self-hosted Render-compatible target on machines you own. This is the shape that absorbs exactly the workload class Fir leaves behind with the most headroom. A bring-your-own-Dockerfile platform on your own hardware takes the same artifact, keeps the registry inside your perimeter (no more amd64-only, Cedar-only constraints — your nodes, your architectures), and removes the per-app dyno meter entirely. The self-hosted field here is mature: Coolify (roughly 56,000 GitHub stars) handles git-push Docker deploys with SSL and reverse proxy on any VPS, Dokku and Kubero cover the Kubernetes-flavored end, and Convox publishes an explicit Heroku migration path it rates low-difficulty.

What changed in 2026 is not the tooling — it is the forcing function. Self-hosting your container workloads used to be a cost optimization; on a frozen Cedar registry, it is an exit from a dead end.

What maps cleanly versus what needs replatforming:

Heroku surfaceAt a Dockerfile-native targetEffort
The Dockerfile itselfBuilds as-isNear zero
Env vars / config varsRe-created as env vars or secretsMechanical
Custom domains + TLSRe-pointed DNS, platform-managed certsMechanical
Heroku Postgres / Redis add-onsManaged equivalent or self-hostedMedium — plan the data move
Procfile process types (web/worker)Separate services or process configSmall per app
Heroku SchedulerCron service or external schedulerSmall
Review apps / pipelinesPreview environments at the targetVaries by target

The only row that deserves real planning is data. Stateless container cutovers are DNS flips; Postgres moves are dump/restore windows with sequence checks. Schedule the data-bearing apps mid-sequence — not first (you want the runbook proven on stateless apps) and not last (you want rollback room while Heroku is still warm).

What can stay — and the traps

Bucket two and three apps can stay on Cedar through sustaining mode without shame. Salesforce is explicit that existing behavior continues, enterprise subscriptions renew, and support remains. And the September 2026 arrival of Cloud Native Buildpacks on Cedar is a genuine bridge: buildpack apps can modernize their build story without touching Fir's sharp edges. If your Heroku estate is mostly standard-framework web apps on buildpacks, "stay and watch" is a defensible 2026 posture — provided you avoid the traps.

Trap one: assuming Fir is your eventual home. Any roadmap slide that ends with "then we move to Fir" needs a footnote for bucket one: those apps never move to Fir. Fir's CNB-only build story is a philosophical choice, not a missing feature awaiting a quarter. Plan as if Docker-on-Fir never ships, because nothing in the sustaining-mode announcement suggests Heroku is building it.

Trap two: the amd64-only registry. Cedar's Container Registry never gained multi-arch support, and now it never will. If any part of your pipeline assumes ARM images — Apple Silicon CI runners, Graviton cost comparisons, the industry-wide ARM drift — the Cedar registry is already the odd one out in your toolchain. Every new Dockerfile you write against it inherits a constraint the rest of the ecosystem abandoned.

Trap three: treating "sustaining" as "static." Sustaining engineering covers stability and security patches to the platform, not to your images. Your Dockerfile-built images still need base-image rebuilds on your schedule, with no rebase safety net. And while Heroku's side stays frozen, your dependencies do not: the next major Postgres, Redis, or framework upgrade lands on the same Cedar surface with no new platform features to ease it. Budget the toil honestly.

Trap four: the renewal mirage. Existing enterprise contracts renew, which procurement will read as "Heroku is fine." It is fine the way a fixed-rate mortgage is fine — the terms hold, but you cannot remodel. The day a new business requirement needs a platform feature Heroku does not have — a region, a compliance posture, a runtime capability — the answer is not a roadmap conversation. It is a migration you should have sequenced earlier.

The migration you sequence beats the migration you're forced into

Heroku's arc from 2007 pioneer to 2010 Salesforce acquisition to the November 2022 end of the free tier to the February 2026 freeze is, among other things, a lesson in exit timing. Every prior inflection gave teams a graceful window — and every window looked, at the time, like something you could sit out. The teams that left after the free-tier removal migrated on their own schedule with rollback room. The teams still on free-tier-era architectures in 2026 are migrating the same workloads under worse conditions.

This window is the same shape with a clearer map. The freeze tells you Cedar never improves. Fir's missing Docker support tells you where Cedar's container users never go. The only question left is sequencing, and the answer fits in one sentence: containers first, because they are stranded and portable; buildpacks later, because they are sustained and entangled.

Run the inventory this week. You may find bucket one is two apps and an afternoon — in which case congratulations, the double bind barely touches you. Or you may find it is half the estate, in which case you now know it in September 2026 instead of discovering it the week something breaks.

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

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex