---
id: platform/migrate-from-heroku
title: Migrate from Heroku
description: Map Heroku process types and configuration to Bex services, choose a build path, and rehearse data and traffic migration.
keywords: [bex, migrate, heroku, config vars, procfile, migration guide]
last_updated: 2026-09-23
---

A Heroku migration recreates processes and dependencies on Bex, then moves data
and traffic. Heroku's Platform API, CLI credentials, `app.json`, and add-ons do
not become Bex resources by changing an endpoint or renaming a file.

Bex is in active development and its upstream project does not yet recommend
production workloads. Begin with a non-production rehearsal and check
[compatibility](./compatibility.md) and required capabilities before proceeding.
After migration, the [CLI reference](./cli-reference.md) documents every `bex`
command and whether it works on Bex.

## Map processes and dependencies

| Heroku concept | Bex setup |
| --- | --- |
| Web process | [Web service](./web-services.md) with an explicit start command and listener port. |
| Worker process | Separate [background worker](./background-workers.md); configure its own environment. |
| Continuously running clock process | Usually a worker. Rewrite as a finite command before converting it to a cron job. |
| Scheduler task | [Cron job](./cron-jobs.md) with an explicit schedule and command that exits. |
| Release process | A pre-deploy command on the chosen service, subject to Bex's execution constraints. |
| Config vars | [Environment variables or groups](./secrets.md); secrets must be transferred deliberately. |
| Postgres add-on | [Bex Postgres](./postgres.md) plus a tested data transfer. |
| Redis-compatible add-on | [Bex Key Value](./key-value.md), after checking data and client requirements. |
| Other add-ons | Retain an external provider or provision a replacement; validate credentials, billing, and network access separately. |
| Review apps, Private Spaces, enterprise identity | Evaluate the required behavior explicitly; these are not automatic mappings. |

Read all entries in the application's [Procfile](https://devcenter.heroku.com/articles/procfile),
including release and clock processes. Record config vars, runtime/buildpack
versions, database extensions, queue semantics, file storage, domains, callback
URLs, and integrations. Processes in different Bex services do not automatically
share configuration.

## Choose a build path

Use a supported native runtime with explicit build/start commands, or build an
image with a Dockerfile. Heroku buildpacks and slugs are not interchangeable with
a Bex build command or container image. For Docker builds, put dependency
installation in the Dockerfile; a `buildCommand` does not replace its build steps.

This illustrative Node.js app has a web process and worker:

```text
web: npm run start
worker: npm run worker
```

A corresponding Bex `render.yaml` can use:

```yaml
services:
  - type: web
    name: shop
    runtime: node
    repo: https://github.com/acme/shop
    plan: starter
    autoDeployTrigger: off
    buildCommand: npm ci
    startCommand: npm run start
    healthCheckPath: /healthz
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: shop-db
          property: connectionString
  - type: worker
    name: shop-worker
    runtime: node
    repo: https://github.com/acme/shop
    plan: starter
    autoDeployTrigger: off
    buildCommand: npm ci
    startCommand: npm run worker
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: shop-db
          property: connectionString
databases:
  - name: shop-db
    plan: starter
    postgresMajorVersion: "16"
```

Replace the repository, commands, plans, and database version with values for
your application. This example assumes a lockfile, the two npm scripts, and a web
listener at `0.0.0.0:$PORT` with `/healthz`. Add a production build step if needed.
If your worker needs a queue, configure that dependency too.

Do not start real workers or schedulers during rehearsal. Use isolated test data
and disable side effects. If several services share a database, assign migration
ownership to one deployment path rather than running the same release command
concurrently from every service.

Validate and apply using the [Blueprint guide](./app-resource.md). An `app.json`
file is an inventory aid, not a Bex manifest. Check every deployment result and
exercise application behavior before moving traffic.

## Transfer config vars and credentials

Copy required config vars through Bex's [secrets interface](./secrets.md). Replace
database and queue URLs with destination connections, and check provider-specific
settings such as allowed origins and OAuth callbacks. Share values deliberately
through an environment group or per-service configuration.

Keep credentials out of the manifest and migration logs. A fresh generated value
is not a substitute for an existing session-signing or encryption key when old
sessions or encrypted data must keep working.

## Move data and traffic

Use the [data-transfer rehearsal](./migrate-from-render.md)
and [cutover sequence](./migrate-from-render.md):
they apply to PostgreSQL, queues, files, and DNS regardless of the source host.
Practice the dump/restore and writer handoff on disposable databases with the
[Postgres transfer rehearsal](./postgres-transfer.md).
Use Heroku's source connection or a compatible logical backup, not a Bex physical
snapshot endpoint as a generic importer.

Heroku [maintenance mode](https://devcenter.heroku.com/articles/maintenance-mode)
blocks normal incoming web traffic, but it does not stop workers or other
background writers. Stop or pause every source writer separately for a final
logical dump/restore. Rehearse restoration, verify data, and enable destination
work only once.

Add domains through [Bex domain setup](./custom-domains.md), follow returned DNS
instructions, and verify ownership, certificates, and responses. Do not assume
Heroku's existing DNS target is the destination target.

Keep the source available during observation. Once the destination accepts new
writes, reverting DNS alone cannot reconcile diverged data. Define that recovery
plan before cutover, then retire Heroku resources explicitly after acceptance.

## Update automation

Rewrite Heroku Platform API integrations for the actual Bex operations in the
[API guide](./api.md). Heroku authentication and API schemas are different;
there is no generic base-URL substitution. Recreate deploy hooks, notification
subscriptions, scheduled tasks, and secret rotation procedures as supported by
the destination installation.

See [Bex vs Heroku](/compare/heroku) for product evaluation and
[Render parity](./render-parity.md) for the implementation boundaries behind
Bex's Render-style workflows.
