Job heroku-app-and-postgres-to-new-host · revised 11 October 2026
Move one Heroku web app and its Postgres database to a host you control
Move one Heroku web app and its Postgres database to a host you choose. A backup you restore is reconciled table by table, and a cutover with a rehearsed way back is agreed first.
You might be seeing
- You have chosen a new host but have no list of everything the Heroku app depends on
- The database is in Heroku Postgres and nobody has tested a restore elsewhere
- Add-ons and the release command would be forgotten in a move because they do not live in the code
No passwords, keys, card details or admin invites needed to start.
What usually happened
A Heroku app is more than its code. It is a Procfile with a web process, a release command that Heroku runs when a release is created (except a release caused only by an add-on's config change), config variables set by you and by add-ons, the add-ons themselves, and a database whose data has to be copied and checked. Moving only the code leaves add-on settings behind and can leave migrations unrun. Heroku's database backups are logical dumps, and restoring one deletes everything in the target database first, so a restore aimed at the wrong database loses data. An app that also runs workers, scheduled jobs, Redis or a queue has more to move and check, and is quoted separately from this fixed job.
Who it’s for: A founder or engineering lead who runs one production web app on Heroku and has decided to move it to hosting they control, because of cost, control or a platform decision.
Usually starts when: The Heroku bill or plan no longer fits, a customer or investor asks where the data lives, the app is on the Heroku-22 stack and the team would rather change host than update the stack, or the team has already chosen another host and nobody has time to plan the move.
The result: The app's web process runs on a host you control, with its database restored from a Heroku logical backup that you capture and restore yourself and reconciled table by table, and its named business flows passing on a preview. You hold a written cutover plan with a write freeze, a backup taken at the freeze and kept as the restore point, a DNS switch for your domain holder and a way back that was rehearsed, and the old app stays untouched until you sign off.
Check whether this job fits
Five short questions. Your answers stay on this page unless you choose to email them.
Checks you can run yourself
List the process type names
Run this in a copy of the project folder. It prints the process type names from the Procfile and nothing else.
cut -d: -f1 ProcfileLook for: Names such as web and release. A name such as worker or clock means the app is outside this fixed job.
List the add-ons
Run this with the Heroku command-line tool, logged in as you, replacing the app name. It lists add-on names only.
heroku addons --app YOUR-APP-NAMELook for: The add-on names and plans. Send the list; it holds no secrets.
List config variable names without their values
Run this and read only the names. Never paste the output: the values are secrets.
heroku config --json --app YOUR-APP-NAME | node -e "console.log(Object.keys(JSON.parse(require('fs').readFileSync(0,'utf8'))).join('\n'))"Look for: A list of names such as DATABASE_URL. The command prints names only; never paste the values.
What you get
- The dependency inventory with a decision for every item
- The new host's build and process configuration as files in your repository, delivered as a pull request
- The reconciliation script, the backup and restore commands, and the rehearsal results with per-table counts and checksums
- The cutover plan and the way-back plan, naming the restore point, who does what and when
Included
- One Heroku app in Ruby, Node.js or Python with one web process type, an optional release command and up to two portable add-ons that hold no data; no workers, schedulers or background runners (including ones that run inside the web process), no Redis or queues
- One Heroku Postgres database up to 20 GB, copied by a logical backup that you capture, keep and restore yourself (or your site holder does) into a PostgreSQL service on the new host that you create; we never receive the backup file or its rows
- An inventory of the Procfile, release command, buildpacks, config variable names (never values) and add-ons, with a replacement or a decision for each
- A build and release process on the new host that runs the same release command and starts the web process
- A read-only reconciliation script that you run on the Heroku database and on the restored copy, giving per-table row counts and checksums over agreed columns, which you send back as plain results
- A cutover plan with a write freeze, a backup taken at the freeze and kept as the restore point until sign-off, and a way back that is rehearsed on a second restored copy
Not included
- Workers, schedulers or background runners (including ones that run inside the web process), Redis or queues, and add-ons that hold data, which are quoted separately
- Creating or paying for the new host account, which you hold
- Heroku Private Spaces or Shield apps, regulated data or apps with more than one database
- Rewriting the app for a new framework version or a new language runtime
- Zero-downtime or live-replication moves, which need a separate plan
- Moving mailboxes or changing email providers, and DNS work beyond the agreed switch
How we know it’s done
Agreed with you before work starts. Each check produces evidence you keep.
Restore test: a backup that you captured with writes paused is restored into a new, empty database you hold, and the reconciliation script reports the same row count for every table on the Heroku database and on the restored copy, and matching checksums over the agreed columns of the five largest tables
Evidence: A table of table names with Heroku and restored counts and checksums, produced by you with our script, and the restore log
The app builds on the new host from the repository, the release command finishes successfully, the web process is running with every config variable name from the inventory present, and each named business flow completes on the access-restricted preview
Evidence: Build log, release command output, a process list and the flow results from the preview
With test rows written to the new database after a rehearsal switch, the reverse step applies those rows to a second empty database restored from the same Heroku backup, and its per-table counts equal the backup's counts plus the test rows, with no duplicate keys and no change to the live Heroku database
Evidence: Before and after counts from the rehearsal, and the rehearsal log of the reverse step
After the domain holder switches DNS, the public site loads over https and each named business flow completes, the reconciliation run at the write freeze matched on every table, and the freeze-time backup's checksum at sign-off equals the checksum recorded when it was taken
Evidence: Status checks, screenshots or redacted records, the freeze-time reconciliation table and the backup's file name, size and checksum at both moments
Sign-off. You review the reconciliation table, the preview results, the rehearsed way back and the cutover plan before your domain holder switches DNS. You sign off after the post-switch checks pass; the freeze-time backup stays with you until then, and the old Heroku app is yours to remove afterwards.
If it fails. If the agreed acceptance checks do not pass, you do not pay and you keep the inventory, the rehearsal results and the way-back plan. The Heroku app stays as it was.
When it fits, and when we stop
It fits when
- The app is Ruby, Node.js or Python and starts from a Procfile or a container image, with a web process and no worker, scheduler or background runner
- The Heroku Postgres database is up to 20 GB, the size Heroku's backup tool is designed for, and holds no regulated data
- You or someone you trust can run the Heroku command-line and restore commands we give you, run our read-only reconciliation script and send back its results, so we never need a login to your Heroku account, a database URL or a copy of your data
- You can create the new host account and a PostgreSQL service on it, and an authorised domain holder can switch DNS
- You can agree a short write freeze for the final data copy and keep the backup taken at the freeze until sign-off
We stop and tell you if
- The database is larger than 20 GB, has very many schemas or large objects, or a backup cannot finish, which needs a different copy method
- The app runs a worker, a scheduler or a background runner, uses Redis or a queue, or depends on an add-on that holds data: this fixed job does not cover them, so ask for a quote
- The app depends on an add-on with no workable equivalent on the new host and no accepted alternative
- Records must never be lost and no write freeze is possible, which needs a separate replication plan
- The app stores customer files on the Heroku filesystem that cannot be found elsewhere, or relies on features of the Heroku platform we cannot reproduce
What could go wrong
The Heroku app and database stay unchanged and available until you sign off. The backup taken at the write freeze is the agreed restore point: you keep it untouched in a place you control until sign-off, and the rehearsal has already restored it once into an empty database. Only one side accepts writes at any moment, and the cutover plan names which. If the new host fails after writes resume, the records written there since the switch are exported and applied to the Heroku database by your database owner before DNS is pointed back; pointing DNS back alone would lose them. That step is rehearsed on a restored copy first, never on the live Heroku database. Restoring the freeze-time backup over the live Heroku database deletes everything in it, including any later records, so it is the last resort and is used only after the new records have been exported.
Scroll the table sideways to read it all.
| Risk | How we handle it |
|---|---|
| Both copies of the app accept writes after the switch, or neither does | The cutover plan names which side accepts writes at each moment, with a write block on the side that is not live, and the rehearsal runs through it. |
| The restore overwrites the wrong database | Heroku's documentation says a restore deletes all data in the target first, so restores are only run into a new, empty database that you can throw away, never over the live Heroku database. |
| The new host's PostgreSQL lacks an extension or a schema the dump refers to | The rehearsal finds this before cutover; Heroku's guide documents the heroku_ext schema issue and we check the extensions list in advance. |
| Config variable values are exposed during the move | Only names are shared with us. You enter values on the new host yourself. |
| The way back damages the Heroku database | The reverse step is rehearsed on a second restored copy first, and the freeze-time backup stays untouched as the restore point until you sign off. |
A second reviewer checks the dependency decisions, the restore options, the reconciliation script, the restore point and the cutover plan, including which side accepts writes at each moment.
How we deliver
We arrange the work and independent review, then show you the result against the agreed checks. You keep authority over your systems.
- Agree the new host, list every dependency of the app, confirm there is no worker, scheduler, queue or data-holding add-on, and decide the replacement for each add-on and config variable group
- Give you the commands to capture and download the Heroku backup to a place you control, to restore it into an empty database on the new host with the options that skip Heroku-specific ownership, and the read-only reconciliation script; you run them
- Receive only the reconciliation results and a schema-only listing, check the extensions and warnings, and write down every per-table difference
- Build the app on the new host from the repository, run the release command and start the web process on an access-restricted preview address with outgoing email, payments and webhooks disabled or captured, then run the named business flows
- Rehearse the cutover with you: a write freeze, a backup taken at the freeze and kept as the restore point, a restore of it into a fresh empty database and the reconciliation again
- Rehearse the way back on a second restored copy with test rows written on the new side, have the plan independently reviewed, then hand it over; the domain holder makes the switch
This is a one-off job, not emergency cover or a subscription. We confirm eligibility, the total price, a start window and a delivery date before you accept. Work starts only after agreed inputs, secure access, any licences and necessary permissions are in place. Hosting, platform and supplier charges are excluded unless the written quote includes them. No charge or booking is created by an enquiry.
Need to keep it working?
Discuss a monthly service that keeps the runtime and dependencies on supported versions on the new host.
Ongoing work is separately scoped and quoted: no monitoring, response-time guarantee or automatic subscription is included in this job.
Explore an ongoing engineering lane, or mention the responsibility you need in your enquiry.
What you can check
This is a new service. We have not delivered this job for a client yet.
Other ways to get this done
- If the only deadline is the Heroku-22 stack, updating the app to a newer stack and staying on Heroku is a valid choice; Heroku's stack page lists the stacks it supports and the command to change stack. This job is for teams that have decided to move. devcenter.heroku.com
- Heroku documents how to take a logical backup of Heroku Postgres and restore it into another PostgreSQL database. If your team can run and verify that process, the data copy alone is a do-it-yourself task. devcenter.heroku.com
- If your app is a Rails app and you want to move to Render, our separate Heroku exit page offers a narrower fixed-price move with its own scope, limits and price. Neither offer includes workers, schedulers or queues. syntheticindustry.ai
Questions
Do you need my Heroku login or my database?
No. You run the commands we give you, keep the backup file yourself and send us only counts, checksums and logs. We do not hold a Heroku account and never receive a database dump.
My app has a worker or scheduled jobs. Can you move it?
Not as this fixed job. Workers, scheduled jobs, Redis and queues are quoted separately. Ask for a quote and list everything that runs besides the web process.
How is this different from your Rails-to-Render page?
That page is a separate, narrower offer for one Rails app moving to Render, with its own price and limits. This job covers Ruby, Node.js or Python apps moving to a host you choose, and its price includes a restore and a way back that are rehearsed. Neither includes workers, scheduled jobs or queues. Both prices are untested proposals.
Which new host do I need?
One you choose and own that can run your language and a PostgreSQL service. Tell us your choice in the enquiry; if the host cannot run the app we say so before quoting.
Will the site go down?
There is a short write freeze for the final data copy that you agree in advance. A move with no pause at all needs live replication, which is a separate plan.
What happens to the old Heroku app afterwards?
It stays as it was until you sign off, and so does the backup taken at the freeze. Removing the app, and the cost of it running meanwhile, is your decision.
Send an enquiry
Send us
- The language and framework, and the new host you have chosen or want advice on
- The process type names in your Procfile and whether a release command is set
- The names of the add-ons, taken from Heroku's add-on list
- The size of the database as Heroku reports it, and the business flows that matter most
Later, once you agree
- A controlled copy of the source with secrets removed, through the agreed company-controlled secure handoff
- The list of config variable names, with values entered by you directly on the new host and never sent to us
- The reconciliation script's results from the Heroku database and from the restored copy: table names, row counts and checksums over agreed columns, plus a schema-only listing of names and types. No backup file, rows or database URL
- A preview environment on a new host account you own, with access restricted and outgoing email, payments and webhooks disabled or captured before any restored data is used; either with rehearsal-only access if you choose to grant it, or your site holder applies our configuration and sends back the logs
- A domain holder available for the switch
- A company-controlled secure handoff agreed before access: no live passwords, keys, private code or customer records by ordinary email.
You own the Heroku account, the new host account, the domain and every secret. We do not hold a Heroku account and never need your Heroku login. You run the backup, restore and reconciliation commands we give you, keep the backup file in a place you control and send us only counts, checksums and logs; we do not receive credentials, database URLs or raw database dumps. Config variable values are entered by you on the new host, and production on the new host is configured and released by you or your site holder. We never ask for passwords, database URLs or tokens in the first enquiry.
Email fallback: open your mail app
If website submission is unavailable, review and send the fallback email yourself. An email fallback is not a website receipt. Or write to hello@syntheticindustry.ai with “heroku-app-and-postgres-to-new-host” as the subject.