Skip to main content

Heroku Fir Doesn't Take Docker: The Migration Audit for Teams Stranded Between Cedar and Fir

9 min readDora NodaDora Noda
Share
On this page

Heroku's newest platform can't deploy your app, and its oldest one is frozen in time. If your team ships with a Dockerfile and heroku.yml, you are standing on the exact square of the board that both generations abandoned — and the migration plan that pretends otherwise will fail on its first deploy.

Here is the situation in one table, substantiated in full below:

How you deploy todayCan it go to Fir?Can it stay on Cedar?
heroku.yml / Container Registry Docker imageNo — container stack unsupported on FirYes, but frozen: no new features, ever
Classic buildpacks with community customizationsOnly if a CNB equivalent existsYes, but frozen
Standard buildpacks, no DockerYes — rebuilt as Cloud Native BuildpacksYes, but frozen

If you are in the first row, this post is your audit checklist: the five things to inventory before anyone promises you a lift-and-shift, where Dockerfile-first workloads actually go, and what the bill looks like once dyno pricing leaves with the platform.

What Fir dropped, precisely

Fir is Heroku's first genuine architecture change since Cedar shipped in 2011: Kubernetes underneath, Cloud Native Buildpacks producing OCI images, OpenTelemetry wired in natively. It went generally available in April 2025. And its own documentation is blunt about the boundary. Per Heroku's Generations page, Fir exclusively uses Cloud Native Buildpacks for builds — classic Heroku buildpacks are unsupported there, and building Docker images with heroku.yml and the container stack is unsupported on Fir. The Container Registry & Runtime documentation carries the matching header: the entire Docker-deploy feature is only applicable to Cedar-generation apps.

So the Docker path is a Cedar-only world, and it is narrower than that. The registry runtime is amd64-only, which has always meant Apple-Silicon developers cross-build or CI-build for deploy, and Fir itself runs on ARM-based Graviton infrastructure — the two generations do not even agree on a CPU architecture. A container image built for one is not casually portable to the other, and there is no tooling bridge because Salesforce never built one: the migration story for Docker-deploy teams is "rebuild your pipeline around Cloud Native Buildpacks," full stop.

One genuine bridge did appear, late: on September 3, 2026, Heroku shipped Cloud Native Buildpack support for Cedar-generation apps, so a Cedar app can now adopt the CNB build contract without moving to Fir. That is useful preparation — it lets you prove your app builds as a CNB while still running where it runs today — but it is not an exit. It runs on the frozen platform, under the frozen economics, and Docker images still deploy only the old way.

Why does any of this force a decision now rather than someday? Because Cedar is where Docker deploys live, and Cedar is frozen.

On February 6, 2026, Heroku chief product officer Nitin T Bhat announced the platform was moving to a "sustaining engineering model" — stability, security, reliability, and support, with no new features and no new Enterprise contracts for new customers. Existing subscriptions are honored and pay-as-you-go signups still work, but the direction is one-way.

Every month a Docker-deploy app stays on Cedar, it accrues migration debt on a platform whose owner has stated, in writing, that the platform will not grow. And Fir, the only growing thing, cannot take it.

The five-point audit

Before anyone quotes you a migration timeline, run this inventory. Each item is a real failure mode observed in Heroku-to-self-hosted moves, not a formality.

1. Deploy method per service. List every Heroku app and label it: heroku.yml Docker, classic buildpack, or CNB-on-Cedar. Teams consistently discover their "three Docker services" are actually seven once review apps, schedulers, and one-off workers are counted. Anything in the Docker column needs a new build pipeline, not a new platform — the platform move is the easy half.

2. Architecture assumptions. If you deploy Docker images, confirm what architecture they target. The Cedar registry path is amd64-only; Fir is Graviton/ARM-based; your developers' laptops are almost certainly ARM. Your replacement pipeline must produce multi-arch images or pin the target explicitly, or you will trade a deploy-time success for a runtime exec format error on the first pod that lands on the wrong silicon.

3. Classic-buildpack customizations without CNB equivalents. Heroku's own documentation warns that many community classic buildpacks have no Cloud Native Buildpack equivalent yet. If your Cedar app depends on a community buildpack for apt packages, binary vendoring, or language-version pinning, check for a CNB replacement before you commit to any CNB-only destination — Fir or otherwise. Each missing equivalent is custom Dockerfile work you must schedule, and it is routinely the longest pole in the migration tent.

4. The add-on long tail. Postgres, Redis, scheduler, logging, monitoring, email — list every add-on per app. This is the inventory step that doubles migration timelines when discovered late: the app code moves in a week, and then the team spends a month re-homing data services and scheduled jobs. Choose the data home before moving any app code, because everything else points at DATABASE_URL.

5. Fir-only features you assumed, if any. If any service was built expecting Fir capabilities — IPv6-by-default networking, native OpenTelemetry collection, Fir dyno types — those assumptions must be re-provisioned at the destination rather than rediscovered as outages. Most Docker-deploy teams have none, which is itself the finding: Fir offers your workload shape nothing, so the destination decision reduces to where Docker-first deploys run best.

Where Dockerfile-first workloads actually go

The workload Fir stranded — a container image built from a Dockerfile, deployed as a unit — is the most portable artifact in the industry. That is the good news hidden inside the gotcha: the thing Heroku won't take is the thing every other platform accepts without conversion.

What a Docker-first destination needs to provide is short: accept an image or a Dockerfile, give it a public HTTPS URL with managed TLS, wire environment variables and attached data services, and expose a deploy API simple enough that git push (or one CLI command) ends in a running service. Render's deploy model is the reference shape — Dockerfile-first, preview environments, straightforward service definitions — and a Render-compatible deploy API means migration tooling, scripts, and muscle memory transfer instead of being rewritten.

On infrastructure you own, that shape runs on Kubernetes via a git-push layer: the same OCI image your CI already builds, scheduled onto nodes you rent at flat monthly rates, behind an ingress controller that terminates TLS exactly the way Heroku's routing mesh did. Nothing about the container changes. The build pipeline you already have — the one Fir rejected — becomes the deploy pipeline unchanged. That is the whole pitch, and it is honest precisely because it asks so little: keep building images, point them somewhere that accepts images.

The bill delta, worked

Take a typical small production setup on Cedar: two Standard-2X web dynos at $50 each, one Standard-2X worker at $50, and a Standard-tier Postgres at $50. That is $200 a month before logging, monitoring, or any other add-on — for 3 GB of dyno RAM and one managed database, on a platform that will never ship another feature. Nothing forces a move this weekend, but everything built on Cedar from today on accrues migration debt at full face value.

The same containerized workload on a single flat-rate dedicated machine — an 8-core, 64 GB class box in the roughly $50–70-a-month band — runs all three services with headroom for roughly two dozen more of the same size, with the database as managed external spend you choose directly. The break-even is not at some distant scale; a single Standard-2X dyno's $50 already rents the better part of the whole box. And the deeper complaint migrators voice is not even the level, it is the shape: per-dyno meters compound silently as services multiply, while the owned box costs the same whether it holds three containers or thirty.

Be fair about what the dyno bill bought beyond compute: Heroku operated the control plane, the routing mesh, and the incident response. Self-hosting moves that on-call burden to you or your platform layer. Price the destination honestly — machine cost plus whoever answers the pager — and the delta still favors owning the box for any team with more than a dyno or two, which is exactly the team profile that outgrows per-unit metering first.

Move in this order, migrate once

Sequence matters more than speed. First, adopt Cloud Native Buildpacks on Cedar where you can — the September 2026 support makes this possible without leaving the platform — so the build contract is proven before anything moves. Second, re-home data: pick the database destination and cut over DATABASE_URL consumers deliberately, because data gravity pulls everything else. Third, move stateless Docker services to the Dockerfile-first destination, lowest-risk service first, keeping Cedar as the instant rollback target. Fourth, migrate scheduled jobs and workers, which hide the most assumptions about filesystem ephemerality and clock behavior. Decommission Cedar last, service by service, with the rollback path live until the final cutover.

The container-registry gotcha only strands teams that treat migration as a platform swap instead of a pipeline audit. Your images were never the problem — they are the most standard artifact you own. The problem was a roadmap that rebuilt the platform around everything except them. Audit the five points, keep building the images, and point them at infrastructure that takes Docker as a first-class answer rather than a legacy exception.

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