HEROKU / MIGRATION

How to Migrate a Web App and PostgreSQL from Heroku

Rehearse the restore, verify the app, and switch traffic

Moving an HTTP app and PostgreSQL off Heroku means mapping how the app starts, how it receives configuration, and where its data lives. This guide uses KamuiDash as the destination. Rehearse on a separate URL and an empty database before scheduling the final cutover.

The guide uses official documentation and a local dump-and-restore check. The Heroku production migration and KamuiDash cloud restore were outside that test.

In this article

Inventory the services you need to move

Heroku component What to check
Web process Runtime, dependencies, startup command and PORT
PostgreSQL Version, extensions, capacity, permissions and connections
Config Vars Secrets, runtime settings and build-time values
Workers, Scheduler and add-ons Replacement service, queue and shutdown behavior
Email, Redis and files Keep or migrate each dependency
Domains and webhooks DNS, callback URLs, retries and switch order
Inventory the source · Conceptual diagram
Startup
Configuration
Database and integrations

Map the Procfile to deployment commands

Heroku's Procfile declares process types. For a Node.js app, web: npm start starts the HTTP process and release: npm run migrate runs migrations during the release phase. On KamuiDash, specify setup (npm ci and any build command), pre-start work if appropriate, and startup (npm start). Listen on the supplied PORT and bind to 0.0.0.0. Verify when pre-start commands run and use a safe migration strategy if more than one replica can attempt the same schema change. Heroku Procfile

KAMUIDASH · SCREENSHOT
KamuiDash application creation screen with runtime language and GitHub repository selectors.
An existing KamuiDash example shows where to select the runtime and source repository.

Move configuration by purpose

Replace the database connection with the destination's connection. Verify which database the new URL actually uses before testing writes. Check external email, authentication and payment integrations, as well as CORS and callbacks. Configure build-time settings separately where needed. Preserve signing and encryption keys when existing sessions or data depend on them. Heroku Config Vars

Rehearse the PostgreSQL restore on an empty target

Use a dedicated empty KamuiDash database with external access available on Pro. Install the KamuiDash CLI and PostgreSQL client tools. Initialize an external password for the new dedicated database if the CLI requests it.

Terminal
kamui login
kamui db connect 'YOUR_PROJECT/YOUR_DATABASE'

In psql, run SELECT current_database(), current_user; to verify the target and exit with \q. The interactive command opens psql. If the running daemon does not list the target, wait for existing users to finish before restarting it, or start a tunnel from a separate environment. To run pg_restore, use the persistent local tunnel. Check the daemon status first and start it if needed.

Terminal
kamui db daemon start
kamui db daemon status

Find the target's local port and the CA path reported by the CLI. The example uses the local TLS behavior checked with CLI 0.1.22. KamuiDash CLI · Connection implementation · Tunnel implementation

Capture and download the Heroku backup

Use the source attachment name in place of DATABASE_URL when it differs. Replace b123 with the capture ID. Confirm the backup is complete and belongs to the intended database before downloading. Use different output filenames for rehearsal and final cutover. Heroku recommends PGBackups for moderately loaded databases of 20 GB or less. For Advanced, use the appropriate logical backup procedure. Heroku PGBackups · Heroku logical backups

Terminal
export HEROKU_APP_NAME='your-source-app'
heroku pg:backups:capture DATABASE_URL --app "$HEROKU_APP_NAME"
BACKUP_ID='b123'
heroku pg:backups:info "$BACKUP_ID" --app "$HEROKU_APP_NAME"
heroku pg:backups:download "$BACKUP_ID" --app "$HEROKU_APP_NAME" --output migration.dump
pg_restore --list migration.dump

Protect the dump as production data and keep it out of Git. Listing the archive is an initial check. Rehearse a restore before the production migration. Heroku export guide

Restore to the dedicated database

Add a service to ~/.pg_service.conf. Replace the example port, database name, user and CA file with the external connection details for your target. Keep the tunnel running and enter the password at the prompt.

Code
[kamui-migration-target]
host=127.0.0.1
hostaddr=127.0.0.1
port=32000
dbname=YOUR_EXTERNAL_DB_NAME
user=YOUR_EXTERNAL_DB_USER
sslmode=verify-full
sslrootcert=/ABSOLUTE/PATH/SHOWN/BY/CLI/ca.pem
gssencmode=disable

Run SELECT current_user; through the destination app’s own database connection and replace YOUR_APP_DB_ROLE with that role name. Switching to that role lets the app own the restored tables and sequences. If SET ROLE fails, stop before restoring and confirm an authorized restore path with your database administrator.

Terminal
psql 'service=kamui-migration-target' -W \
  -c 'SELECT current_database(), current_user, version();'

export KAMUI_APP_DB_ROLE='YOUR_APP_DB_ROLE'
psql 'service=kamui-migration-target' -W -v ON_ERROR_STOP=1 \
  -v "app_role=$KAMUI_APP_DB_ROLE" <<'SQL'
SET ROLE :"app_role";
SELECT current_user;
SQL

pg_restore \
  --role="$KAMUI_APP_DB_ROLE" \
  --dbname='service=kamui-migration-target' \
  -W \
  --no-owner --no-privileges \
  --exit-on-error --single-transaction \
  migration.dump

The restore skips source ownership and grants, so apply any permissions the app needs afterward. A single transaction avoids a partially applied restore on failure. Check extension support, client and server version compatibility, permissions, sequence values, and the duration and locks for larger databases. Use the same tunnel and test data to verify the route before the final migration. PostgreSQL pg_dump · PostgreSQL pg_restore

Verify the app and data at the new URL

Check representative records, relationships, timestamps, ID sequences, login, writes and updates. Test file storage and external integrations separately.

KAMUIDASH · SCREENSHOT
Details of a static site on KamuiDash: status, public URL, GitHub branch, and deployment history by commit.
An existing static-site example shows where to find the public URL and deployed commit. Verify actual reads and writes on the migrated app.

Freeze writes before the final cutover

Production cutover · Conceptual diagram
Pause writes
Capture and restore
Verify, switch and resume
  1. Announce the maintenance window. Stop writes from the old app, jobs, webhooks and other clients.
  2. Capture a final backup and restore it to an empty database prepared for the cutover.
  3. Connect the new app to the final database and verify it while keeping writes paused on the old system.
  4. Switch the domain and webhook targets. Monitor residual traffic to the old service.
  5. Resume writes on the new system only.

Heroku maintenance mode blocks HTTP routing while dynos keep running. Fir apps need their own way to block writes because maintenance mode is unavailable. Record the current formation, stop jobs and workers that write, and let active transactions finish before capturing the final backup. Keep writes disabled on the new service too, including its default URL and jobs, and use read-only checks before the switch. Heroku maintenance mode

DNS caching can send requests to both services during the switch. If the app cannot pause writes, plan continuous synchronization and reconciliation. After new writes begin, rolling back requires preserving those changes as well as switching the URL. Decide the rollback deadline, how to pause writes, and how to reconcile the new data before starting.

Frequently asked questions

Do Heroku add-ons move with the app?

Inventory each add-on. Workers, mail, queues and schedules can require separate migration or configuration.

When should I run schema migrations?

Restore the dump to an empty database first. Match the application version to the restored schema and apply only the additional changes you need.

Does this apply to Ruby and Python?

The inventory and database steps apply. Adapt the dependency, build, start and migration commands to your runtime.

KamuiDash is a Tokyo-region PaaS for publishing web applications through GitHub and AI integrations.