Aurora PostgreSQL speaks the same wire protocol and runs the same SQL as the Postgres you already have — so this is a homogeneous migration with no schema conversion. The real work is extension and version compatibility, picking the right move-the-data method (snapshot, logical replication, RDS read-replica promotion, or DMS), and rehearsing a cutover you can roll back. This page walks all four, with honest downtime and cost numbers — and how a MAP-funded partner can run the whole thing at little-to-no cost.
Aurora PostgreSQL is a PostgreSQL-compatible engine that AWS re-built on a purpose-designed distributed storage layer. You keep PostgreSQL — the SQL, the extensions, the drivers, the ecosystem — and swap the storage and replication internals for something built to scale reads, survive failures, and recover faster.
The single most important fact about this migration: it is homogeneous. Aurora PostgreSQL is wire-compatible with community PostgreSQL, so the same psql client, the same JDBC/psycopg/pgx drivers, the same ORM, and the same SQL all work unchanged. There is no schema conversion, no datatype remapping, no PL/pgSQL rewrite. That is the entire reason a Postgres→Aurora move is an order of magnitude less risky than a heterogeneous move like Oracle→Aurora or SQL Server→Aurora, where the AWS Schema Conversion Tool (SCT) and weeks of stored-procedure rework are the main event.
The reasons teams make the move cluster into four buckets. Read scaling: up to 15 low-latency read replicas share one copy of the storage volume, so adding a replica does not duplicate the data and replica lag is typically single-digit milliseconds rather than the seconds RDS streaming replicas can show under load. Durability and recovery: data is written six ways across three Availability Zones, storage self-heals, and failover to a replica is usually 30 seconds or less versus the one-to-two minutes typical of RDS Multi-AZ. Storage that manages itself: the volume auto-grows in 10 GB increments to 128 TiB with no pre-provisioning and no resize downtime.
The fourth bucket is operational economics: Aurora separates compute from a storage layer you never size, supports Serverless v2 for spiky or unpredictable load (fine-grained capacity that can sit at a low floor between bursts), offers Global Database for sub-second cross-region replication, and clones a multi-terabyte database in minutes via copy-on-write for test environments. None of these change your application code.
The honest counterpoint, stated up front: if your database is small, steady, and single-AZ-tolerant, plain RDS PostgreSQL may be cheaper and is perfectly fine — Aurora earns its keep when you need the read fan-out, the faster failover, the self-managing storage, or Serverless v2 elasticity. Section VII lays the costs out side by side so the decision is made on numbers, not vibes.
Because the SQL ports cleanly, the genuine compatibility surface is narrow but sharp: which PostgreSQL extensions you depend on, which major version you are on, and a short list of behaviours Aurora handles differently from community Postgres.
Aurora PostgreSQL ships a large set of supported extensions — pgvector, PostGIS, pg_stat_statements, pg_partman, pg_cron, hstore, citext, uuid-ossp, the postgres_fdw / dblink family, pglogical, and many more — but the supported list and the exact supported version of each extension are tied to the Aurora PostgreSQL engine version. The first task in any real assessment is to enumerate what the source actually uses (query pg_extension and check what your application loads) and confirm each one is available, at a compatible version, on the target Aurora engine. This is the number-one source of late surprises.
Two classes of extension need explicit attention. First, anything that depends on loading arbitrary C libraries or shared_preload_libraries that a managed engine does not allow — heavily custom or niche third-party extensions can be unsupported on both RDS and Aurora, and that is a finding you want in week one, not at cutover. Second, version-sensitive extensions: pgvector in particular has moved quickly (index types and operators have evolved across releases), so if you run vector search you want source and target on versions whose pgvector behaviour matches, or plan a controlled reindex.
Version strategy matters because logical replication and DMS CDC both have version expectations. Native PostgreSQL logical replication generally wants the source on PostgreSQL 10+ (publications/subscriptions) and is smoothest when source and target majors are close — replicating into a newer major is usually fine, the wrong direction is not. The clean approach is to land on Aurora at a major version equal to or one step above the source, validate, then do any major-version upgrade as a separate, well-understood operation afterwards rather than combining a platform move and a big version jump in one cutover.
A few behavioural differences are worth knowing but are rarely blockers: Aurora manages or ignores storage/replication GUCs that have no meaning on its architecture (you do not tune full_page_writes or WAL archiving as you would on self-managed Postgres); parameter and minor-version management happens through DB cluster and instance parameter groups, not postgresql.conf; and you get the rds_superuser role rather than raw SUPERUSER. If your app or migration tooling assumes SUPERUSER or direct filesystem/WAL access, that assumption needs revisiting — and it needs revisiting for RDS too.
Before choosing a method, run the inventory: SELECT extname, extversion FROM pg_extension; on every source database, list any shared_preload_libraries, note the exact source major.minor version, and confirm each extension is supported at a compatible version on the target Aurora PostgreSQL engine. Extension/version mismatches — not data movement — are what derail homogeneous migrations.
All four land you on the same place: an Aurora PostgreSQL cluster running your schema and data. They differ on downtime, on which sources they support, and on how much rehearsal they need. Pick on those three axes.
The routing question CloudRoute (and any competent partner) asks first is: where does the source live, and how much downtime can you tolerate at cutover? Those two answers narrow four methods to one or two. Below, in rising order of complexity.
How: For an RDS PostgreSQL source, take an RDS snapshot and restore it directly as an Aurora PostgreSQL cluster — AWS migrates the data for you. For self-managed or off-AWS Postgres, run pg_dump and restore into Aurora with pg_restore (parallelized with -j). Either way the database is offline for writes from the moment you take the consistent copy until the app is pointed at Aurora.
Downtime: equal to copy + restore + validation time — minutes for a few GB, hours for hundreds of GB to a few TB.
Best for: small-to-mid databases, dev/test environments, and any workload that can take a planned window. Lowest complexity, lowest risk — the default when a short outage is fine.
How: If the source is already RDS PostgreSQL, create an Aurora PostgreSQL Read Replica of it. AWS seeds Aurora from the RDS data and keeps it in sync. When lag reaches zero, you stop writes briefly, let Aurora catch up, promote the replica to a standalone cluster, and repoint the app.
Downtime: seconds to a couple of minutes — only the brief quiesce-and-promote window, not the data copy.
Best for: the common case of an RDS PostgreSQL estate moving to Aurora. It is the cleanest near-zero path because AWS manages the seeding and sync end to end. The catch: the source must be RDS PostgreSQL (not self-managed or other-cloud) at a major version Aurora supports as a replication source.
How: Use PostgreSQL's built-in logical replication (a publication on the source, a subscription on Aurora) — or pglogical for more control. Load the initial copy, then logical replication streams ongoing changes into Aurora until you flip. This is Postgres replicating to Postgres on the engine's own machinery, so it is high-fidelity for standard tables.
Downtime: near-zero — a short cutover once the subscription is caught up.
Best for: near-zero moves from any Postgres (self-managed on EC2/on-prem, Heroku/Crunchy/Supabase, Cloud SQL for PostgreSQL) where you want to avoid a separate migration service. Constraints: it does not replicate sequences (advance them at cutover), large objects and some DDL need handling, every replicated table needs a primary key or a defined REPLICA IDENTITY, and it wants the source on a modern major (10+).
How: AWS Database Migration Service does a full load of existing data, then change data capture (CDC) to replicate ongoing writes until cutover. For homogeneous Postgres→Postgres there is no Schema Conversion Tool step — the schema is already compatible (create it with pg_dump --schema-only or via your migration plan), and DMS can use PostgreSQL logical replication under the hood.
Downtime: near-zero — flip when CDC latency is at or near zero.
Best for: sources that are self-managed, on another cloud, or otherwise not RDS — and migrations where you want one managed service to orchestrate full-load + CDC, monitor latency, validate row counts, and run several databases in parallel. The usual choice when method 2 is unavailable and you do not want to hand-run logical replication. Caveats: DMS moves table data well but is not a complete schema/constraint/sequence tool by itself, wide or LOB-heavy tables need tuning, and you should run DMS data validation before trusting the cutover.
For the three near-zero methods, the migration is 90% waiting for sync and 10% a tightly choreographed cutover. The discipline that separates a clean flip from an incident is the runbook around that window.
A sound cutover sequence looks the same regardless of which CDC method you used. Let the target reach steady-state with replication latency at or near zero and hold it there under real production load long enough to trust it — a few days to a couple of weeks for a busy database. Validate continuously while you wait: compare row counts per table, checksum or sample critical tables, point a read-only copy of the app or your analytics queries at Aurora, and watch the DMS or logical-replication lag metrics.
At the flip: drain writes on the source (brief read-only/maintenance mode), let replication drain the last transactions to zero latency, advance any sequences on the target to match the source (logical replication and DMS do not carry sequence values automatically), run final integrity checks, then repoint the application to the Aurora cluster endpoint. A DNS CNAME or a centrally-managed connection string in front of the endpoint makes both the flip and the rollback a single change rather than a redeploy.
The rollback plan is non-negotiable and the reason senior teams sleep through cutover night. Keep the source intact and writable-on-demand until you have soaked on Aurora through at least one full business cycle. If something is wrong post-cutover — a missed extension behaviour, a query-plan regression, an app assumption you did not catch — repointing back to the source must be a sub-minute operation. The risk window is the gap between "writes now go to Aurora" and "we are confident enough to decommission the source"; design it to be cheap to reverse, not heroic.
Two recurring gotchas worth pre-empting. First, connections: if you front Aurora with RDS Proxy or a pooler, validate it before cutover — connection-storm behaviour and failover semantics differ from a single RDS instance. Second, statistics: run ANALYZE on the Aurora cluster after the load, because a freshly loaded database with stale stats can produce alarming query plans that resolve the moment statistics are current — a surprise best met during the soak, not during the flip.
Aurora gives you two ways to run the same PostgreSQL-compatible engine: classic provisioned instances (you pick the instance class) or Serverless v2 (capacity scales in fine-grained increments between a floor and a ceiling you set). The right choice depends on how predictable your load is.
Provisioned Aurora is the default for steady, predictable workloads. You size the writer and readers to your baseline, you pay for that capacity continuously, and you get the most predictable per-hour cost. If your database runs at a fairly constant utilization all day, provisioned is usually the cheaper and simpler choice — and you can still attach Serverless v2 readers later if a reporting workload needs to flex independently.
Aurora Serverless v2 measures capacity in Aurora Capacity Units (ACUs) and scales them up and down in small steps in response to load, between a minimum and maximum you configure. It shines for workloads that are spiky, bursty, or unpredictable — a SaaS app with sharp business-hours peaks and quiet nights, dev/test/staging environments that should idle cheaply, new products without a known traffic profile, and multi-tenant fleets where individual databases have very different and unpredictable demand. You pay for the capacity actually consumed at fine granularity, so you stop paying for headroom you only need at peak.
The practical pattern for a migration is to land on the shape that matches the workload, not to over-think it: steady production → provisioned, sized to baseline with a couple of readers for fan-out; spiky or unknown production → Serverless v2 with a sensible floor and a ceiling above your worst peak; non-prod → Serverless v2 with a low floor so it costs little when idle. A frequent and effective mix is a provisioned writer for the predictable baseline plus Serverless v2 readers that absorb bursty read traffic. Whatever you choose, set the minimum capacity deliberately — too low a floor on a latency-sensitive production database can mean a cold scale-up exactly when traffic arrives.
Concretely, here is the shape of a logical-replication or DMS CDC migration from a busy production PostgreSQL database to Aurora PostgreSQL, the way a partner runs it.
1 — Inventory & compatibility. Enumerate every source database, its exact major.minor version, every extension (extname/extversion) and any shared_preload_libraries, the workload shape (read/write ratio, peak concurrency, largest tables, LOB usage), and every application connection string. Confirm each extension is supported at a compatible version on the target Aurora engine. This is the step that prevents cutover-night surprises.
2 — Target design. Choose the Aurora PostgreSQL engine version (equal to or one major above the source), provisioned vs Serverless v2, writer/reader topology, the parameter group, networking (VPC, subnets, security groups), and whether RDS Proxy fronts the cluster. Provision the cluster and apply the parameter group.
3 — Schema load. Create the target schema (pg_dump --schema-only → restore, or your IaC/migrations), install required extensions on Aurora at matching versions, and confirm every replicated table has a primary key or a defined REPLICA IDENTITY so CDC can track row changes.
4 — Initial load + CDC. Full-load the existing data (DMS full-load, or initial copy + logical-replication subscription), then start CDC so ongoing writes stream to Aurora. Monitor replication latency until it sits at or near zero.
5 — Soak & validate. Hold the target in sync under real production load for days-to-weeks. Compare row counts and checksums, point read-only traffic and analytics at Aurora, run ANALYZE, and watch lag and error metrics. Fix any extension-behaviour or query-plan surprises now, with the source still authoritative.
6 — Cutover. Quiesce writes on the source, drain the last transactions to zero latency, advance sequences on the target, run final integrity checks, and repoint the application (DNS/CNAME or central connection string) to the Aurora endpoint. Total write-downtime: seconds to a couple of minutes.
7 — Stabilize & decommission. Keep the old source intact and reversible through at least one full business cycle. Once Aurora is proven, retire the source, finalize backups/retention and monitoring on Aurora, and right-size capacity based on observed load.
The honest cost story is that Aurora is roughly comparable to RDS PostgreSQL on compute and storage per unit, prices I/O differently, and removes a large slice of the human-time and risk cost that dominates self-managed Postgres on EC2 or on-prem. Decide on total cost, not the per-hour sticker.
On compute, Aurora and RDS PostgreSQL are in the same ballpark per vCPU-hour for comparable classes; Aurora often carries a modest premium that buys the distributed storage layer, faster failover, and read fan-out. On storage, both bill per GB-month, but Aurora's volume is self-managing (auto-grows, no pre-provisioning, no resize downtime) while RDS storage is pre-provisioned. The dimension people miss is I/O: Aurora bills storage I/O pay-per-request by default, and offers Aurora I/O-Optimized — a configuration that removes per-request I/O charges in exchange for higher compute/storage rates, which is cheaper for I/O-intensive workloads (often a meaningful fraction of Aurora spend) and worth modelling explicitly.
Against self-managed PostgreSQL on EC2 or on-prem, the instance can look cheaper on paper — until you add the parts you now own: building and testing HA and failover, patching and minor-version upgrades, backup/restore and PITR engineering, storage scaling and its downtime, replication, monitoring, and on-call when any of it breaks at 3am. Aurora and RDS fold those into the managed price. For most teams the self-managed "savings" are eaten by engineering time and the risk of an HA story that has never actually been failover-tested.
Decide by modelling 12-month total cost on your real numbers — instance class, storage GB, observed I/O, replica count, plus a realistic line for the DBA/SRE hours self-management consumes — across all three options, factoring Serverless v2 if your load is spiky (a bursty workload that idles at night can land well below a provisioned instance sized for peak). The table below frames the axes; the next section is where the cost can drop to near-zero for the migration itself.
Even though Postgres→Aurora is technically clean, most teams have not run a near-zero-downtime production cutover with a tested rollback before — and they want it run by someone who has. That is what CloudRoute routes you to, and the AWS Migration Acceleration Program is what can make it cost little-to-nothing.
CloudRoute is a routing layer, not the migrator. You describe the source (RDS / self-managed / Heroku / Cloud SQL), the database size and workload, your downtime tolerance, and your timeline; CloudRoute matches you to a vetted AWS partner with a documented PostgreSQL→Aurora track record and the relevant tier (Advanced or Premier for MAP-scale engagements), in your region. The partner runs the inventory, target design, data-movement method, soak, cutover, and rollback plan.
The funding hook is the AWS Migration Acceleration Program (MAP). MAP runs in three phases — Assess (TCO and readiness; the extension/version inventory and method choice live here, and this phase is often AWS-funded), Mobilize (landing zone, pilot, runbook), and Migrate & Modernize (production cutover) — and AWS provides credits/funding scaled to the migration size, paid to the partner through the AWS Partner Network. For a qualifying migration, that funding can cover a large share of the engagement, which is how the customer migrates at little-to-no cost.
The honest framing, stated plainly: MAP funding applies to qualifying migrations, which typically means a meaningful committed AWS spend after migration (a single small database may be too small on its own, though it often qualifies as part of a broader move to AWS). Where MAP applies, the migration is effectively funded; where it does not, CloudRoute is still a vetted-partner referral that de-risks the cutover — and CloudRoute is paid by the partner, not by you, either way. These MAP mechanics tie into the broader AWS credits story, which is why migration and AWS POC/credit funding are routed together.
For qualifying migrations, MAP funds the partner-led engagement and the customer pays little-to-nothing for the cutover itself (you still pay for the running Aurora cluster afterwards). Qualification generally requires a meaningful post-migration AWS commitment. Where a workload does not qualify, CloudRoute is a vetted-partner referral that de-risks the move — and CloudRoute is always paid by the partner, never by you.
Same engine, three operating models. Aurora and RDS are both managed PostgreSQL-compatible services; self-managed is PostgreSQL you run yourself on EC2 or on-prem. The differences are in storage, scaling, failover, and how much you operate by hand.
| Dimension | RDS PostgreSQL | Aurora PostgreSQL | Self-managed (EC2 / on-prem) |
|---|---|---|---|
| Engine | Community PostgreSQL (managed) | PostgreSQL-compatible, AWS storage engine | Community PostgreSQL (you run it) |
| Schema conversion to migrate | None (already Postgres) | None — homogeneous, wire-compatible | None (it is Postgres) |
| Storage | Pre-provisioned EBS; resize is an operation | Auto-grows to 128 TiB; 6 copies / 3 AZs; self-healing | You size, scale, and protect disks yourself |
| Read replicas | Up to 5 streaming replicas (own copies) | Up to 15 replicas sharing one volume; ms-level lag | You build and manage streaming replication |
| Failover | Multi-AZ ~1–2 min | Typically ≤30s to a replica | Whatever you build and have actually tested |
| Elastic capacity | Fixed instance class | Provisioned or Serverless v2 (fine-grained autoscale) | Manual rightsizing / re-provisioning |
| I/O billing | Bundled in provisioned storage | Pay-per-request or Aurora I/O-Optimized | Your own disk/IOPS spend |
| Ops burden (HA, patching, backups, on-call) | Low — AWS-managed | Low — AWS-managed | High — you own all of it |
| Best fit | Steady, modest, single-region Postgres | Read fan-out, fast failover, spiky load, scale | Full control / specific OS-level needs / niche extensions |
Situation: Read replicas were lagging under peak load and the team had been scaling the writer instance class up just to survive 9am–6pm, then paying for that headroom all night. They wanted Aurora's read fan-out and Serverless v2 economics for the bursty profile, but nobody on staff had run a production cutover before, the database backed every tenant, and the maximum tolerable write-downtime was a couple of minutes inside a weekend window. pgvector version compatibility was an explicit worry.
What CloudRoute did: Routed within 24 hours to an AWS Premier partner with a documented PostgreSQL→Aurora track record. MAP Assess (AWS-funded) ran the inventory: confirmed pgvector, PostGIS, and pg_stat_statements were all supported at compatible versions on the target Aurora PostgreSQL engine, and pinned the engine one major above the source. Because the source was RDS PostgreSQL, the partner used the Aurora read-replica method — created an Aurora PostgreSQL replica of the RDS instance, let it seed and sync to zero lag, and soaked it under mirrored read traffic for nine days while validating row counts and query plans. Target shape: a provisioned writer sized to baseline plus two Serverless v2 readers to absorb the daytime burst. Cutover quiesced writes, drained to zero lag, advanced sequences, ran final checks, and repointed via the centrally-managed connection string, with a tested rollback to the RDS instance held ready.
Outcome: Cutover completed inside the weekend window with ~70 seconds of write-downtime; rollback was never needed and the RDS source was kept warm for two weeks before decommissioning. Replica lag at peak dropped from multi-second to single-digit milliseconds, and the Serverless v2 readers idling down at night cut the monthly database bill ~28% versus the peak-sized RDS topology. The migration engagement was MAP-funded against the post-migration AWS commitment; CloudRoute's commission was paid by the partner. The customer paid $0 for the migration itself.
size: ~2.3 TB · method: Aurora read-replica + promote · write-downtime: ~70s · monthly DB cost: ~−28% · migration cost to customer: $0 (MAP-funded)
CloudRoute routes you to a vetted AWS partner with a PostgreSQL→Aurora track record. For qualifying migrations the AWS Migration Acceleration Program funds the engagement — customer pays $0 for the migration. No procurement. No discovery theater.