Moving Heroku Postgres to Amazon RDS: What Actually Works When You Don’t Have Superuser

Heroku Postgresql to Amazon Migration

You’ve decided to move. You open the AWS Database Migration Service documentation, follow it carefully, and about twenty minutes in, you hit a wall: DMS wants a user with the REPLICATION role, and Heroku won’t give you one.

That’s not a configuration mistake. Heroku Postgres doesn’t grant customers the superuser role, and it doesn’t let you grant the replication role to anyone else. Heroku’s own support documentation says so directly, and gives that as the reason third-party tools like DMS can’t replicate a Heroku Postgres database to a non-Heroku target. Heroku support has also stated that it doesn’t support logical replication for Heroku Postgres instances.

So the standard playbook is out. Here’s what’s left, what each option costs you, and how to structure a cutover you can actually put in a runbook.

Why this is now a live question

In February 2026, Salesforce announced that Heroku is moving to a sustaining engineering model. Stability, security, and reliability work continues. New feature development stops, and new Enterprise contracts are no longer offered to new customers.

Heroku isn’t going away. Your dynos didn’t stop, your database is still up, and nothing about that changes next quarter. What changed is the roadmap, and roadmap risk compounds quietly. If logical replication wasn’t a Heroku Postgres feature before the announcement, it isn’t going to become one now. Waiting for the platform to solve the migration problem for you is no longer a plan.

The three paths that actually exist

1. Fork, dump, restore

Create a fork of your database so you’re not hammering the production instance, run pg_dump against the fork, then pg_restore into RDS with parallel jobs. Run the whole thing from an EC2 instance in the same region rather than your laptop, and you’ll avoid the timeouts and download bottlenecks that make this miserable.

It’s simple, and it works. It’s also lossy. Every write that lands between the moment you take the fork and the moment you cut over is gone. On a staging database, fine. On a table full of orders or cases, that’s revenue and support tickets you’ll be reconstructing by hand.

2. WAL log shipping to S3

The near-zero-downtime approach involves asking Heroku’s Data team to configure continuous write-ahead log shipping to an Amazon S3 bucket, along with a base physical backup. You then replay those WALs with a tool like wal-g into a staging Postgres instance, keep it caught up, and promote it.

Teams have done this successfully at terabyte scale. But look at the shape of it: it depends on a support arrangement rather than a product feature, it requires you to operate a WAL replay pipeline you’ve never operated before, and it’s built on a workflow that a platform in sustaining engineering has no incentive to make easier. If you have a strong DBA and a large database, it’s a defensible choice. For most teams, it’s more machinery than the problem deserves.

3. Connector-level replication

The third option is to move the data through an ordinary database connection. A replication tool authenticates as a normal Postgres user, reads from the source, and writes to the target on a schedule: no replication slot, no logical decoding plugin, no support ticket.

This path doesn’t run into the privilege problem because it never asks for elevated privileges in the first place.

Where DBSync Cloud Replication fits

DBSync connects to PostgreSQL over standard JDBC using the same kind of credentials your application already uses. That’s the whole trick, and it’s why it works against managed platforms that hold superuser back.

The practical shape of a migration looks like this. You point DBSync at Heroku Postgres as the source and your RDS or Aurora instance as the target. It creates the schema on the target and adapts it as the source changes; existing columns are preserved rather than dropped, so nothing historical disappears when someone cleans up a column upstream. You run a full load, then run incremental passes on a schedule while your application keeps writing to Heroku as normal.

One detail matters more than people expect: DBSync runs either as a cloud-hosted engine or self-hosted on your own infrastructure. If your target RDS instance sits in a private subnet – which it should – the self-hosted option inside your VPC is what makes the connection straightforward instead of a networking project.

What DBSync doesn’t do is read the Heroku write-ahead log. Nothing outside Heroku can. If you need true log-based capture off that source, path two is your only option, and you should go talk to Heroku’s Data team.

A cutover sequence you can put in a runbook

Step 1 – Provision the target and oversize it. Pick an instance larger than your calculations suggest. You can scale down in a month once you have real numbers. Running the migration itself on an undersized instance is how you end up with lock contention you’ll spend a day diagnosing in Performance Insights.

Step 2 – Run the initial full load. No time pressure here. The application is still serving from Heroku.

Step 3 – Start scheduled incremental passes and let them run for several days. This is the step teams skip, and it’s the most valuable one. After four days you know your real delta volume, how long a pass takes at 9am versus 3am, and what breaks. You’ve converted the scariest unknown in the project into a measured quantity.

Step 4 – Compare source and target, and fix what’s wrong. Do this while nothing is on fire. Every mismatch you find now is one you’re not finding at 11pm on cutover night.

Step 5 – Freeze, run a final incremental, compare again. The freeze window is short because steps 3 and 4 did the work. This is where you find out whether all that preparation paid off, and it usually has.

Step 6 – Repoint DATABASE_URL and bring the application up against RDS.

Step 7 – Keep the reverse path configured but idle. Define a rollback window – a week is typical – during which you could push back to Heroku if something surfaces that testing didn’t catch. A rollback plan that only exists as a paragraph in a document isn’t a rollback plan.

Get the RDS side right before you move anything

A few target-side decisions that are much cheaper to make now than to change later.

Region. Heroku Postgres databases in the US region run on AWS in us-east-1. If your new RDS instance lands in us-east-2 because that’s your company default, every byte of the migration crosses a region boundary, and so does every incremental pass until you cut over. Sometimes that’s the right trade for latency to your users or an existing VPC. Just make it a decision rather than an accident.

Enforce SSL on the instance. Change the parameter group to require SSL connections before the first byte moves, not after. Retrofitting that once applications are connected means a coordinated restart you didn’t budget for.

Lock the security group down from the start. Migration is exactly when someone opens 0.0.0.0/0 “just for today,” and it stays open for a year. Allow the specific source you’re replicating from and nothing else.

Check your extension list. Run SELECT * FROM pg_extension; on the source and confirm every one of them is available on your chosen RDS engine version. This is the item most likely to derail the plan, and it takes five minutes to check.

Validation is the step everyone skips

Row counts are not validation. Two tables can have identical counts and completely different contents, and a count(*) will tell you everything is fine right up until finance asks why the quarterly numbers moved.

DBSync Data Compare handles this part: it compares records between source and target, surfaces mismatches, and gives you something concrete to attach to a go-live decision. That’s the actual point. Not the tooling, but the fact that somebody has to sign off, and “the app looked fine on Monday” isn’t evidence.

If you’re not using DBSync for this, use something. Write a checksum script per table. Sample and diff the twenty tables that matter most. The specific method matters far less than having a gate at all.

Decide these five things before you start

  • How much downtime can you actually take? Not the number your product manager wants. The number your business will tolerate at 2am on a Sunday.
  • How big is the database, really? Include bloat and index size, not just the number in pg_database_size.
  • Do you need a rollback path? If the answer is yes, that shapes the tooling choice more than anything else on this list.
  • Where does the target live? A private-subnet RDS instance changes how the replication layer has to be deployed.
  • Who signs off on data parity? Name the person before you start, not the week of cutover.

Answer those five honestly and the right path usually picks itself. A 20 GB staging database with an acceptable two-hour window doesn’t need any of this – take a fork, dump it, restore it, and go home. A 400 GB production database with a Salesforce dependency and a finance close in three weeks needs the full sequence above.


Moving off Heroku Postgres and want a second opinion on the sequence? Try DBSync Cloud Replication in the Playground – no download, no credentials – or book a 30-minute migration review with one of our engineers.

Rishav Tiwary

Product Manager @ DBSync | Building Enterprise SaaS for Data Integration, Replication & Automation