← Blog

Migrating Heroku Postgres without a maintenance window

Railyard

July 29, 2026

Every other part of a Heroku migration is mechanical: config vars copy-paste, the Procfile reads the same way, buildpacks build the app unchanged. The database is the one piece that actually worries people, because it’s the one piece where “it didn’t work” means lost data, not just a redeploy.

It’s more boring than it sounds. Here’s exactly what happens.

What the importer actually does

Applications → Import reads a Heroku account from an API token and shows a plan per app before anything runs — what comes over, what you’ll need to add yourself. For Heroku specifically: config vars, the full formation (process types and their counts), and Postgres, Redis, MySQL and Memcached add-ons all come across, along with custom domains.

The one thing Heroku’s API genuinely can’t hand over is the Git URL itself — Heroku doesn’t expose your repository, so that’s the one field you supply yourself during import.

How the data actually moves

The database is copied by streaming a dump straight from Heroku’s DATABASE_URL into the new managed Postgres database on Railyard — not a manual pg_dump to a laptop and back up, a direct stream between the two. That matters because it means there’s no multi-hour file sitting on someone’s disk as the one copy of production data mid-migration.

The sequence that actually avoids downtime:

  1. Run the import. The new app exists, configured, with its own fresh database already streaming from the old one.
  2. Keep the Heroku app running the whole time. Nothing about the import stops it.
  3. Once the new DATABASE_URL answers correctly and you’ve verified it against the old one, move DNS.
  4. Only then suspend the Heroku dynos — after the cutover, not before.

There’s no maintenance window to schedule because there’s no moment where neither database is the one serving traffic. The old one keeps answering until the new one is proven, and the switch itself is a DNS change, not a downtime event.

What still needs a human

Secrets stored as Heroku config vars come over with everything else — but if your app pulls credentials from anywhere Heroku’s API doesn’t expose (a buildpack-injected value, something set outside config vars entirely), that’s on you to re-check. And if the database has extensions beyond stock Postgres — pgvector, PostGIS — confirm the target supports them before cutting over, not after.

For the fastest path, heroku config -s output pastes directly into Variables → Bulk import if you’re doing any part of this by hand instead of through the importer.

Full mapping for every source platform: Import from Heroku, Render, Fly or Railway and the complete walkthrough at Migrating from Heroku.

Import a Heroku app directly.

Paste an API token, review the plan per app, and the formation, add-ons and domains come over before you touch the database.

Free plan, no card, and 14 days of Pro for every new team. One flat price per team.