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 when a vetted partner can file MAP funding for the move (only when your Postgres runs outside AWS today).
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 to 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. CloudRoute matches you with that partner. Whether AWS funds the move depends on where your Postgres runs today.
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 route is the AWS Migration Acceleration Program (MAP). MAP has three phases: Assess (readiness and a TCO model; the extension and version inventory and the method choice live here), Mobilize (landing zone, pilot, runbook) and Migrate and Modernize (production cutover). MAP is for moves onto AWS, so a source on Heroku, Cloud SQL, another cloud or your own servers can qualify. Your matched partner files the MAP request when the migration qualifies, and AWS decides. For startups, partner-filed funding is typically $50K to $150K. Depends on the workload; AWS approves.
The honest framing, stated plainly: a move from RDS PostgreSQL to Aurora stays inside AWS, so it is not a MAP migration. A single small database from outside AWS may also be too small on its own; it has a better chance as part of a broader move to AWS. If the Aurora move comes with new work on AWS, such as a new product or a new customer, ask the partner about incremental workload (IW) funding. Where no funding applies, a vetted partner still de-risks the cutover. CloudRoute is free for you either way. Partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
MAP funding applies only to a qualifying move onto AWS, and AWS approves it. It offsets the migration work, not the running Aurora cluster afterwards. A move from RDS PostgreSQL is not a MAP migration, so plan to pay the partner for that work. CloudRoute never charges you: partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
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 |
You fill in a short form: what you run today, what you want to move, test or build, your stage and your region.
We read your answers and pick one vetted AWS consulting partner that fits the work, the stack and the region. We reply within 24 hours; your matched partner reaches out within 5 business days.
Your matched partner scopes the migration with you. Then it checks whether the move qualifies for MAP (Migration Acceleration Program) funding. MAP is for teams that are not on AWS yet.
If the work qualifies, the partner files the funding request with AWS. AWS reviews it and decides. The amount depends on the workload.
CloudRoute is free for startups. Partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
AWS Activate credits are separate, and you apply for them yourself. Use Activate Founders if no investor backs you. Use Activate Portfolio with the Org ID or invite from your VC, accelerator, incubator or angel group if it is an Activate Provider. A consulting partner cannot apply for Activate on your behalf.
A vetted AWS partner with a PostgreSQL to Aurora track record runs the move. If your Postgres runs outside AWS today, the partner checks whether the move qualifies for MAP funding: typically $50K to $150K for startups. Depends on the workload; AWS approves. CloudRoute is free for startups.