heroku postgres → rds · 2026 cutover guide

Heroku Postgres to RDS — the three cutover methods, the runbook, and the downtime math.

Moving the database is the part of a Heroku migration that keeps people up at night — it is the one piece you cannot roll forward from if it goes wrong. This is the focused engineer playbook for Heroku Postgres → Amazon RDS (or Aurora PostgreSQL): which of the three cutover methods fits your database size, how to handle Heroku Postgres specifics (followers, connection limits, pinned extensions, forced SSL, the DATABASE_URL swap), the exact cutover runbook with a rollback, and how to compute your real downtime window. A MAP-funded AWS partner can run the whole cutover — often at little-to-no cost.

cutover downtime
0–10 min
DB sizes covered
1GB–2TB+
project length
1–4 weeks
cost to you (MAP)
$0–low
TL;DR
  • There are three honest ways to move Heroku Postgres to RDS, and the right one is decided almost entirely by database size and how much downtime you can take. Small DB (< ~20GB) and a quiet maintenance window: pg_dump | pg_restore is the simplest reliable path, 10–60 minutes of downtime. Large or busy DB that needs near-zero downtime: AWS DMS with change data capture (CDC) or native PostgreSQL logical replication keeps the new RDS instance continuously synced while you test, then you flip in a 0–10-minute window.
  • Heroku Postgres has specifics that derail a naive migration: it caps connections by plan (60 on Standard-0 up to 500 on the larger tiers), it gives you follower (read replica) databases you must account for, it pins a curated extension set you have to match on RDS, and it forces SSL. The actual production switch is one change — swapping the DATABASE_URL (and removing Heroku's automatic attachment) so the app points at RDS instead of Heroku Postgres.
  • Target choice: RDS for PostgreSQL is the like-for-like landing spot (you pick the version, storage, and Multi-AZ); Aurora PostgreSQL is the upgrade when you want faster failover, storage that auto-grows to 128TB, and read scaling — at a modest premium. CloudRoute routes you to a vetted AWS partner who runs the cutover end-to-end; when the broader migration qualifies for the AWS Migration Acceleration Program (MAP), AWS funds it and the customer pays little-to-nothing.
the stakes

IWhy the database cutover is the part that actually carries risk

You can re-deploy a web app a hundred times in an afternoon; a botched build just rolls back. The database is different. It holds state, it accepts writes continuously, and once you have taken writes on the new instance you cannot quietly undo them. That asymmetry is why the Postgres move deserves its own runbook, separate from the rest of the Heroku migration.

This page is deliberately narrow. The companion playbook (Heroku → AWS) covers the whole move — dynos to ECS/Fargate or App Runner, Redis to ElastiCache, add-ons to AWS-native, buildpacks to containers. Here we go deep on the one component where a mistake is expensive and hard to reverse: getting every row of Heroku Postgres onto Amazon RDS, with the application pointed at it, inside a downtime window you have agreed in advance.

The core tension is between simplicity and downtime. The simplest method (dump and restore) requires a write freeze for as long as the dump-plus-restore takes — fine for a 5GB database at 2 a.m., unacceptable for a 600GB database serving customers across time zones. The near-zero-downtime methods (DMS CDC and logical replication) remove the freeze by continuously replicating live changes into RDS while the old database keeps serving, so the actual switch is just a brief pause to drain in-flight writes and repoint the app. The whole game is choosing the method that buys you exactly the downtime budget you have — and no more complexity than that requires.

Two facts shape every decision below. First, Heroku Postgres is a managed Postgres with guardrails — you do not get superuser, you cannot install arbitrary extensions, and connections are capped by plan — so a few migration techniques that work on self-managed Postgres are simply unavailable, which narrows the method choice in a helpful way. Second, the production cutover itself is almost anticlimactic: it is a single environment change, swapping DATABASE_URL so the app resolves to the RDS endpoint instead of Heroku Postgres. Everything else is preparation and verification around that one swap.

pick by size + downtime

IIThe three cutover methods — and which one your database needs

Almost every Heroku Postgres → RDS migration uses one of three methods. The decision is not about which is "best" in the abstract — it is about your database size and the downtime you can take. Pick the simplest method that fits both.

Read these as a decision tree, not a menu. Start at the top; drop to the next method only when the constraint above forces you to.

Method 1 — pg_dump | pg_restore (small DB, planned window)

How it works: freeze writes (put the app in maintenance mode), run pg_dump against Heroku Postgres in the custom/directory format, stream it into pg_restore against the new RDS instance, verify, then bring the app up pointed at RDS. A common one-shot is pg_dump $HEROKU_DATABASE_URL -Fc | pg_restore --no-owner --no-acl -d $RDS_DATABASE_URL — the --no-owner / --no-acl flags matter because Heroku owns the source roles and you do not want to recreate them on RDS.

Downtime: the full dump-plus-restore time, because writes are frozen the entire time. Roughly 10–60 minutes for databases up to ~20GB on a fast link; longer for larger or heavily-indexed schemas (index rebuilds dominate restore time).

Best for: databases under ~20GB where a 15–60-minute maintenance window at an off-peak hour is acceptable. This is the overwhelming majority of early-stage apps, and you should not reach for anything fancier if this fits.

Watch out for: a logical dump is a point-in-time snapshot — any write after the dump starts is lost unless the app is genuinely frozen, so maintenance mode must be real (block writes, not just hide the UI). Restore on a generously-sized instance and dial it down afterward; CPU and IOPS during restore are far higher than steady state.

Method 2 — AWS DMS with CDC (large/busy DB, near-zero downtime)

How it works: AWS Database Migration Service does an initial full load of the existing data into RDS, then switches to change data capture — it reads Heroku Postgres's write-ahead log and streams every subsequent INSERT/UPDATE/DELETE into RDS continuously. The new instance stays within seconds of the source while you run validation against it. At cutover you stop writes briefly, let CDC drain the last changes (replication lag → 0), then repoint the app.

Downtime: typically 0–10 minutes — only the time to drain final changes and swap DATABASE_URL, independent of database size. A 500GB database cuts over in the same short window as a 5GB one.

Best for: databases too large to freeze (dozens of GB to multiple TB) or apps that take writes around the clock. DMS also has built-in data validation that row-counts and checksums source vs target, which is reassuring on a big move.

Watch out for: CDC against PostgreSQL needs logical replication enabled on the source (Heroku exposes rds.logical_replication-style settings on its newer plans; confirm your plan supports it). DMS is excellent at bulk row movement but does not migrate sequences' current values, certain constraints, or large-object edge cases perfectly — reconcile sequences and re-validate constraints at cutover.

Method 3 — native logical replication (PG-to-PG, near-zero downtime)

How it works: PostgreSQL's own publish/subscribe logical replication. You restore the schema to RDS, create a PUBLICATION on the source and a SUBSCRIPTION on RDS, and Postgres copies the initial data and then streams ongoing changes natively — no extra service in the path. Cutover is the same shape as DMS: drain lag to zero, repoint the app.

Downtime: 0–10 minutes, same as DMS.

Best for: homogeneous Postgres-to-Postgres moves (which a Heroku → RDS migration always is) where you want to avoid standing up DMS, and where source and target major versions are compatible. Engineers comfortable in psql often prefer this for its directness and the absence of a billed replication instance.

Watch out for: it depends on Heroku exposing logical-replication on your plan, and version skew matters (replicating into a much newer major version can surface incompatibilities). Logical replication does not copy sequence positions automatically and historically does not replicate DDL — freeze schema changes during the migration window and advance sequences manually at cutover.

the one-line decision rule

If the database is small enough to freeze for an hour at 2 a.m., use pg_dump | pg_restore and stop. If it is not, use DMS CDC (managed, with built-in validation) or native logical replication (no extra service, engineer-friendly) to get a near-zero-downtime cutover. There is no prize for using the complex method on a database that did not need it.

what bites you

IIIHeroku Postgres specifics that derail a naive migration

Heroku Postgres is managed Postgres with guardrails. Those guardrails change how the migration behaves, and four of them catch teams who treat the source as if it were a vanilla self-hosted database.

Account for each of these before you start copying data — they are far cheaper to handle in planning than to discover mid-cutover.

  • Follower (read replica) databases — Heroku followers are read-only replicas you may have pointed reporting, search indexing, or read-heavy endpoints at. They do not migrate themselves. On AWS the equivalent is an RDS read replica (or an Aurora reader). Inventory every follower and its consumers before cutover, stand up the matching RDS/Aurora replica, and repoint those consumers as part of the switch — a forgotten follower means a dashboard that silently keeps reading the dead Heroku database.
  • Connection limits — Heroku Postgres caps connections by plan — roughly 60 on Standard-0, scaling to ~500 on the larger Standard/Premium tiers. Many Heroku apps therefore run PgBouncer (Heroku's connection pooler) to survive. RDS connection limits scale with instance memory and are configurable, and Amazon RDS Proxy gives you managed pooling on the AWS side. Decide up front whether you carry your pooler over or adopt RDS Proxy, and size max_connections on the target so the cutover does not trip a connection ceiling under real load.
  • Pinned / curated extensions — Heroku only allows a curated set of extensions and pins their versions; you cannot install arbitrary ones. Run \dx on the source to list exactly what is installed (commonly pg_stat_statements, citext, uuid-ossp or pgcrypto, hstore, sometimes PostGIS). RDS supports a broad set but you must CREATE EXTENSION them on the target before/at restore, and confirm version compatibility. A missing extension shows up as a restore error or a runtime failure the first time the app calls a function it provides.
  • Forced SSL and the role model — Heroku Postgres requires SSL connections; your RDS instance should too (enforce rds.force_ssl and ship the RDS CA bundle to the app so verify-full keeps working). And because Heroku manages roles/ownership, dump with --no-owner --no-acl and create your own application role on RDS — do not try to recreate Heroku's internal roles.
the actual switch

IVThe DATABASE_URL swap — the single change that is the cutover

For all the preparation, the production switch itself is one environment change. Understanding exactly how Heroku wires DATABASE_URL is what keeps that change from silently reverting on you.

On Heroku, the Postgres add-on is attached to the app, and that attachment is what sets the DATABASE_URL config var. The add-on also rotates credentials periodically, which means DATABASE_URL can change underneath you — and, critically, if you only overwrite the value while the add-on is still attached, a later rotation (or a config refresh) can stomp your new value and point the app back at Heroku Postgres. The fix is to detach the add-on from the app's automatic DATABASE_URL once you cut over, so nothing rewrites it.

The clean sequence: (1) put the app in maintenance mode to stop writes; (2) confirm replication lag is zero (DMS/logical replication methods) or the dump/restore has completed and verified (dump method); (3) reconcile sequences and re-validate constraints; (4) set DATABASE_URL to the RDS endpoint connection string — including ?sslmode=verify-full and the RDS CA — and remove Heroku's automatic attachment so it cannot be overwritten; (5) run smoke tests (a read, a write, a representative transaction) against RDS while still in maintenance; (6) take the app out of maintenance. Traffic now hits RDS.

A subtlety worth planning for: the application reads DATABASE_URL at boot for most frameworks (Rails, Django, Node connection pools), so changing the config var typically requires a restart/redeploy to take effect. Sequence the restart inside the maintenance window so there is never a moment where some processes are writing to Heroku Postgres and others to RDS — split-brain writes across two databases are the worst failure mode of a cutover, and a single coordinated restart is what prevents them.

choosing the target

VRDS for PostgreSQL vs Aurora PostgreSQL — which target to land on

Both are managed PostgreSQL on AWS and both are valid destinations from Heroku. RDS for PostgreSQL is the like-for-like landing spot; Aurora PostgreSQL is the upgrade you choose when you want better failover, storage that scales itself, and cheaper read scaling.

RDS for PostgreSQL runs the community PostgreSQL engine on managed infrastructure — you pick the exact major/minor version, the instance class, the storage type (gp3 is the sensible default), and whether to enable Multi-AZ for a synchronous standby. It is the closest mental model to Heroku Postgres and the simplest place to start: if your goal is "the same Postgres, but on AWS and cheaper at scale," RDS is the right answer and you can always move to Aurora later.

Aurora PostgreSQL is AWS's PostgreSQL-compatible engine with a re-architected storage layer. The practical wins: storage auto-grows in 10GB increments up to 128TB with no manual resizing, replication is to a shared distributed storage volume so read replicas are cheap and failover is typically faster (often well under a minute), and you can add up to 15 low-lag readers for read scaling. The trade is a modest premium on compute and storage-I/O billing, and slightly less control over the exact engine internals. For a Heroku app that already leans on followers for reads or expects rapid data growth, Aurora often pays for itself; for a steady mid-size workload, RDS is perfectly sufficient.

Because both speak the PostgreSQL wire protocol and SQL, the migration mechanics above are identical for either target — pg_dump/restore, DMS CDC, and logical replication all land equally well on RDS or Aurora. The target decision is therefore an architecture choice you can make on its own merits (failover SLA, growth trajectory, read-scaling needs, budget), not a constraint imposed by the migration method.

the runbook

VIThe cutover runbook — and the rollback you must have ready

A database cutover is a sequenced operation with a rehearsed rollback, not a vibe. Write it down, dry-run it against a staging copy, and have the rollback path decided before you touch production.

A representative near-zero-downtime runbook (DMS CDC or logical replication), in order:

  • T-7 to T-2 days — Provision the RDS/Aurora target, restore the schema, create matching extensions, size max_connections and storage, enforce SSL, and stand up any read replicas to mirror Heroku followers.
  • T-2 to T-1 days — Start full load + ongoing replication (DMS task or logical SUBSCRIPTION). Let it catch up; watch replication lag trend to near-zero and stay there.
  • T-1 day — Run validation against the target: DMS data validation or manual row-count/checksum on the largest tables, plus a full app test suite pointed at RDS. Freeze schema (DDL) changes.
  • T-0, minute 0 — Enable maintenance mode on the app; writes stop.
  • T-0, minute 1–3 — Confirm replication lag = 0; stop the replication task; reconcile sequence values; re-validate critical constraints/foreign keys.
  • T-0, minute 3–6 — Swap DATABASE_URL to the RDS endpoint (with sslmode=verify-full + CA), detach Heroku's automatic attachment, and restart/redeploy so all processes pick up the new connection string together.
  • T-0, minute 6–9 — Smoke test against RDS (a read, a write, a representative transaction, a background-job run). If green, disable maintenance mode — traffic is now live on RDS.
  • T+0 to T+72h — Keep the Heroku database online but read-only as a hot rollback target; monitor RDS connections, CPU, IOPS, and slow queries; right-size the instance down from the migration-sized footprint.
rollback rule

Rollback is only safe before the app takes writes on RDS. Until you exit maintenance, rolling back is trivial — revert DATABASE_URL to Heroku Postgres and restart. After writes land on RDS, you cannot simply switch back without losing those writes; reverse replication (RDS → Heroku) would have to have been set up in advance, which almost no one does. So the real decision gate is the smoke test at minute 6–9: do not exit maintenance until you are confident, because that is the last cheap exit.

compute your window

VIIDowntime-window math — how to size your maintenance window honestly

Teams either over-promise ("zero downtime!") or panic ("we need an 8-hour window!"). Both come from not doing the arithmetic. Here is how to estimate the real number for each method so you can commit to a window you can actually hit.

For the dump-and-restore method, downtime ≈ dump time + transfer time + restore time, all serialized because writes are frozen throughout. Dump and restore are both dominated by data volume and, on restore, by index rebuilds — a useful planning rule is that restore (with index creation) often takes longer than the dump, and total wall-clock runs roughly an hour per ~15–25GB on a healthy link and instance, though heavily-indexed schemas push that up. The only reliable number is the one you measure: restore a recent dump to a throwaway RDS instance and time it. That measured number, plus a safety margin, is your window.

For the near-zero-downtime methods (DMS CDC, logical replication), the full-load and catch-up happen before the window with the app still live, so they do not count as downtime at all. Your actual downtime is only: time to drain final replication lag (seconds-to-low-minutes if you cut over during low traffic) + sequence/constraint reconciliation + the app restart to pick up the new DATABASE_URL + smoke tests. That total is typically 5–10 minutes and, crucially, is roughly constant regardless of database size — which is the entire reason these methods exist.

Two levers shrink the window further. First, cut over during your genuine traffic trough (for a US-centric SaaS, that is usually the early-morning ET hours) so there is less in-flight write to drain and less risk during the restart. Second, rehearse the exact sequence against a staging cutover at least once; the difference between a 6-minute and a 40-minute real cutover is almost always whether someone is improvising. The downtime you should publish to stakeholders is your rehearsed time plus margin — not your best-case hope.

who runs it

VIIIHow CloudRoute fits — a partner runs the cutover, MAP-funded

You can run a Heroku Postgres → RDS cutover yourself with the methods above. Many teams would rather have someone who has done it dozens of times own the runbook, the validation, and the 6 a.m. switch — especially when AWS will fund it.

CloudRoute routes you to a vetted AWS partner who runs the database cutover end-to-end: provisioning RDS or Aurora, choosing the method that fits your size and downtime budget, standing up the replication, building and rehearsing the runbook, executing the cutover in your agreed window, and owning the rollback decision gate. You keep your engineers on product instead of becoming part-time DBAs for a fortnight.

The funding mechanism is the part most teams do not know about. The AWS Migration Acceleration Program (MAP) funds qualifying migrations — AWS typically covers the assessment phase and credits a meaningful share of the migration cost, with the partner paid through MAP — so a qualifying migration can cost the customer little-to-nothing. A standalone Postgres move is usually small on its own; it most often qualifies as part of the broader Heroku → AWS migration (app + database + supporting services), which is exactly the kind of workload MAP is designed to fund. Honest framing: MAP applies to qualifying migrations (typically with a reasonable post-migration AWS-spend commitment); where it does not, CloudRoute is still a vetted-partner referral that de-risks the cutover — the same outcome without the funding.

Either way, the customer is never in the payment loop with CloudRoute: AWS funds the credits and the MAP engagement, the partner delivers the migration, and CloudRoute is paid by the partner. If you are eyeing this database move as the first step of leaving Heroku, the funded path means the riskiest piece is run by specialists and a large share of the bill is on AWS.

method comparison

pg_dump/restore vs DMS CDC vs logical replication

The same three methods, side by side on the variables that actually decide which you use: downtime, the database size each suits, and the risk/effort profile. Pick the simplest row that fits your size and downtime budget.

MethodDowntimeDB size it suitsNear-zero downtime?Risk / effortWhen to use it
pg_dump | pg_restoreFull dump + restore time (≈10–60 min for ≤20GB)Small (≤ ~20GB)No — writes frozen throughoutLow effort, low complexity; risk = data lost if maintenance isn't realSmall DB + an off-peak maintenance window you can take
AWS DMS + CDC0–10 min (drain lag + repoint)Large / busy (dozens of GB to multi-TB)YesMedium effort; managed service with built-in validation; reconcile sequences/constraints at cutoverLarge or 24/7 DB needing near-zero downtime, want managed tooling + validation
Native logical replication0–10 min (drain lag + repoint)Medium–large, PG-to-PGYesMedium effort; no extra billed service; version skew + manual sequences/DDL freeze to watchHomogeneous PG move, engineer comfortable in psql, want no extra service in the path
All three are valid Heroku Postgres → RDS/Aurora paths; the choice is size + downtime, not preference. Whichever you pick, the production switch is the same single change: swap DATABASE_URL to the RDS endpoint and detach Heroku's automatic attachment so it cannot be overwritten.
not sure which method your database needs?
Get matched with a partner who runs the Postgres cutover for you
Start in 3 minutes →
a recent match

A Heroku Postgres → Aurora cutover CloudRoute routed — anonymized

inquiry · series-a b2b saas, remote-US, ~$4K/mo Heroku
Series-A B2B SaaS, 14 engineers, Rails app on Heroku with a ~420GB Heroku Postgres (Premium tier) + two followers feeding reporting

Situation: The database was too large to freeze — customers wrote around the clock across US and EU hours, so a dump/restore window was a non-starter. The team had tried to scope a near-zero-downtime cutover internally but stalled on the Heroku specifics: logical-replication eligibility on their plan, matching a pinned extension set (PostGIS + pg_stat_statements + citext), carrying their PgBouncer pooling over, and a rollback they actually trusted. Nobody wanted to own the 6 a.m. switch on a 420GB production database.

What CloudRoute did: Routed within 20 hours to a US-East partner with a database-migration track record. The partner chose AWS DMS with CDC (for the built-in data validation on a big move), landed the data on Aurora PostgreSQL for faster failover and cheap read replicas to replace the two Heroku followers, adopted RDS Proxy in place of PgBouncer, rehearsed the runbook twice against a staging cutover, and scoped it inside the team's broader Heroku → AWS migration so it qualified for MAP. Cutover ran in the early-morning ET trough.

Outcome: Final cutover downtime: 7 minutes (drain lag → reconcile sequences → swap DATABASE_URL + restart → smoke test). Zero data loss, validated by DMS row-count/checksum + the app suite. Heroku Postgres held read-only for 72h as a rollback target, then retired. Because the move was part of a qualifying MAP migration, AWS funded the engagement and credited the migration cost — the customer's out-of-pocket was effectively $0; CloudRoute was paid by the partner.

engagement window: ~3 weeks · cutover downtime: 7 min · data loss: 0 · method: DMS CDC → Aurora · cost to customer: ~$0 (MAP-funded)

faq

Common questions

Can I migrate Heroku Postgres to RDS with zero downtime?
Effectively yes — "near-zero," typically a 0–10-minute switch. You cannot get a true zero if the app reads DATABASE_URL at boot (it needs one coordinated restart to pick up the new endpoint), but with AWS DMS change data capture (CDC) or native PostgreSQL logical replication the bulk copy happens while the old database keeps serving, so your only real downtime is draining the last few seconds of replication lag, reconciling sequences, restarting, and smoke-testing. pg_dump/pg_restore, by contrast, freezes writes for the whole dump-plus-restore and is only "low downtime" on small databases.
Which method should I use — pg_dump, DMS, or logical replication?
Decide by database size and downtime budget, not preference. Small DB (≤ ~20GB) and an off-peak maintenance window you can take: pg_dump | pg_restore — simplest, stop there. Large or 24/7 DB needing near-zero downtime: AWS DMS CDC (managed, with built-in data validation) or native logical replication (no extra billed service, engineer-friendly). For a homogeneous Postgres-to-Postgres move both near-zero methods land equally well; DMS adds validation tooling, logical replication keeps everything in psql.
Should I migrate to RDS for PostgreSQL or Aurora PostgreSQL?
RDS for PostgreSQL is the like-for-like landing spot — you pick the exact version, instance, storage, and Multi-AZ; it is the simplest "same Postgres, on AWS, cheaper at scale" choice and you can move to Aurora later. Aurora PostgreSQL is the upgrade when you want storage that auto-grows to 128TB, faster failover (often under a minute), and up to 15 cheap read replicas — at a modest compute/I-O premium. Apps that already lean on Heroku followers for reads or expect rapid growth usually justify Aurora; steady mid-size workloads are fine on RDS. The migration mechanics are identical for either target.
What about my Heroku follower databases?
Followers are Heroku's read replicas and they do not migrate themselves. Inventory every follower and what reads from it (reporting, search indexing, read-heavy endpoints), then recreate the equivalent on AWS — an RDS read replica or, on Aurora, a reader instance — and repoint those consumers as part of the cutover. A forgotten follower means a dashboard or job that silently keeps querying the dead Heroku database after you switch.
How do I handle Heroku Postgres connection limits and pooling?
Heroku caps connections by plan (~60 on Standard-0 up to ~500 on larger tiers), so many apps run PgBouncer. On RDS, max_connections scales with instance memory and is configurable, and Amazon RDS Proxy provides managed pooling. Decide up front whether you carry your existing pooler over or adopt RDS Proxy, and size max_connections on the target so the cutover does not trip a connection ceiling the moment real traffic lands.
Will my extensions and SSL settings carry over?
Not automatically. Heroku pins a curated extension set — run \dx on the source (commonly pg_stat_statements, citext, uuid-ossp/pgcrypto, hstore, sometimes PostGIS) and CREATE EXTENSION each on RDS before/at restore, confirming version compatibility. Heroku also forces SSL, so enforce it on RDS too (rds.force_ssl) and ship the RDS CA bundle to the app so sslmode=verify-full keeps working. And dump with --no-owner --no-acl, then create your own application role on RDS rather than recreating Heroku's internal roles.
How exactly does the production switch happen?
It is one change: set DATABASE_URL to the RDS endpoint connection string (with sslmode=verify-full + CA) and remove Heroku's automatic add-on attachment so a later credential rotation cannot overwrite it back to Heroku Postgres. Because most frameworks read DATABASE_URL at boot, you restart/redeploy so every process picks up the new endpoint together — doing this as one coordinated restart inside the maintenance window is what prevents split-brain writes across two databases.
Can I roll back if the cutover goes wrong?
Cheaply, but only before the app takes writes on RDS. Until you exit maintenance mode, rollback is trivial: revert DATABASE_URL to Heroku Postgres and restart. Once writes land on RDS you cannot simply switch back without losing them unless you set up reverse replication in advance (almost nobody does). So treat the smoke test just before exiting maintenance as the real decision gate — that is the last cheap exit. A common safety net is keeping the Heroku database online but read-only for 72 hours post-cutover.
Does AWS really fund this migration?
For qualifying migrations, yes — via the AWS Migration Acceleration Program (MAP). AWS typically covers the assessment phase and credits a meaningful share of the migration cost, with the partner paid through MAP, so a qualifying migration can cost the customer little-to-nothing. A standalone Postgres move is usually small on its own; it most often qualifies as part of the broader Heroku → AWS migration. Where MAP does not apply, CloudRoute is still a vetted-partner referral that de-risks the cutover. Either way you are not in the payment loop with CloudRoute — AWS funds the credits/engagement, the partner delivers, and the partner pays CloudRoute.

Want a funded, near-zero-downtime Heroku Postgres → RDS cutover?

CloudRoute routes you to a vetted AWS partner who picks the right method, rehearses the runbook, and owns the switch — and when the migration qualifies for MAP, AWS funds it. Customer pays little-to-nothing.

cutover downtime0–10 min
matched within< 24h
cost to you (MAP)$0–low
Heroku Postgres to RDS — methods, runbook & downtime (2026) · CloudRoute