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 and required capabilities before proceeding.
After migration, the CLI reference documents every bex
command and whether it works on Bex.
Map processes and dependencies
| Heroku concept | Bex setup |
|---|---|
| Web process | Web service with an explicit start command and listener port. |
| Worker process | Separate background worker; 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 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 must be transferred deliberately. |
| Postgres add-on | Bex Postgres plus a tested data transfer. |
| Redis-compatible add-on | Bex Key Value, 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, 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:
web: npm run start
worker: npm run workerA corresponding Bex render.yaml can use:
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. 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. 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 and cutover sequence: 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. Use Heroku's source connection or a compatible logical backup, not a Bex physical snapshot endpoint as a generic importer.
Heroku 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, 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. 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 for product evaluation and Render parity for the implementation boundaries behind Bex's Render-style workflows.