How Amazon Aurora PostgreSQL and RDS for PostgreSQL differ on storage, failover, read replicas, serverless scaling and cost, for a typical application backend.
Postgres on AWS means choosing between Amazon RDS for PostgreSQL and Amazon Aurora PostgreSQL, often before the application has much data at all. RDS is the standard PostgreSQL engine on an instance with attached storage. Aurora keeps the PostgreSQL interface but replaces the storage layer, which changes how failover, replicas and billing work.
For a backend with a few services and one database server, the right pick depends on traffic shape and how much downtime a failover can cost you. This guide compares the two on the points that decide it, and shows how Encore treats the choice as an environment setting so you can start on one and use the other in another environment.
Each option is covered below, starting with Encore.
Encore combines an open source infrastructure SDK for TypeScript and Go with a platform that provisions and deploys into your own AWS or GCP account. When you create an AWS environment, you choose its database server: Amazon RDS Postgres, Amazon Aurora Postgres, or an existing instance you import. Every database your application declares lives on that server as its own logical database, with a separate Postgres role for each service.

The service code has no idea which one it runs against:
// orders/orders.ts
import { api, APIError } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
// RDS or Aurora on AWS, depending on the environment. A local Postgres under encore run.
export const db = new SQLDatabase("orders", { migrations: "./migrations" });
interface Order {
id: number;
totalCents: number;
}
export const get = api(
{ expose: true, method: "GET", path: "/orders/:id" },
async ({ id }: { id: number }): Promise<Order> => {
const row = await db.queryRow<Order>`
SELECT id, total_cents AS "totalCents" FROM orders WHERE id = ${id}`;
if (!row) throw APIError.notFound("order not found");
return row;
},
);
The migration in orders/migrations creates the orders table the queries use:
-- orders/migrations/1_create_orders.up.sql
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
total_cents INTEGER NOT NULL
);
Run in your terminal to get started locally.
A new RDS server starts on db.m5.large with Multi-AZ enabled and 7 days of backups. A new Aurora cluster starts with one writer on db.r6g.large, and from the environment's infrastructure settings you can add up to 15 readers, switch to I/O-Optimized storage, or set an instance to db.serverless with a capacity range down to 0 ACUs. Both are encrypted at rest with deletion protection on, and both sit in private subnets that only the application's compute subnets can reach.
The choice is made when an environment is created, so a common setup is RDS for staging and Aurora for production. Migrations run on every deploy against whichever one the environment uses, and encore run starts Postgres locally so they are tested before either sees them.
With the database declared in code and not in a Terraform module, a coding agent asked to add an orders table writes the migration and the query in one change, runs it locally, and never has to know whether production is on Aurora.
RDS for PostgreSQL runs the community PostgreSQL engine on a DB instance with EBS storage. You pick the instance class and storage size, turn on storage autoscaling if you want it, and choose high availability: a Multi-AZ instance keeps a synchronous standby in another Availability Zone, and a Multi-AZ DB cluster adds two readable standbys.
RDS runs the same PostgreSQL you run locally, extensions behave as documented upstream, and pricing is an instance price plus storage, with no per-request charge. Read replicas each keep their own copy of the data, so they add storage cost and can lag behind the primary under heavy writes. Failover to a Multi-AZ standby typically takes one to two minutes, which some backends cannot accept.
Aurora separates compute from storage. The cluster volume keeps six copies of your data across three Availability Zones and grows as you write, so there is no disk to size. Readers attach to the same volume, which keeps replica lag low, and promoting a reader during failover is faster because no data has to be copied.
Aurora Serverless v2 lets an instance scale in Aurora capacity units within a range you set, which suits spiky traffic and development databases that sit idle at night. The trade-off is cost: per Aurora pricing, Aurora Standard bills for I/O requests on top of instance and storage, and I/O-Optimized drops that charge for higher base prices. For a steady, modest workload, a single RDS instance is usually cheaper.
Aurora is compatible with PostgreSQL, not identical to it. A few extensions and parameters differ from community PostgreSQL, so check the Aurora PostgreSQL extension list if you depend on something unusual.
Pick Encore when you want Postgres in your own AWS account, with RDS in one environment and Aurora in another from the same application code.
Pick RDS for PostgreSQL for steady workloads where cost predictability and upstream PostgreSQL behavior matter most. Pick Aurora when you need several low-lag read replicas, faster failover, storage you never size, or Serverless v2 for traffic that comes and goes.
RDS for PostgreSQL runs the standard PostgreSQL engine on an instance with attached EBS storage. Aurora PostgreSQL is PostgreSQL-compatible but uses AWS's distributed storage layer, which keeps six copies of the data across three Availability Zones, grows automatically and lets up to 15 read replicas share the same storage.
Usually, for a small or steady workload. Aurora instances cost more per hour than comparable RDS instances, and Aurora Standard also charges per I/O request. Aurora I/O-Optimized removes the I/O charge for a higher instance and storage price, and Aurora Serverless v2 can scale down to zero ACUs when idle.
With Encore, you declare the database with new SQLDatabase("orders", { migrations: "./migrations" }) and choose Amazon RDS Postgres or Amazon Aurora Postgres when you create the AWS environment. The application code and migrations are the same for both, and encore run starts a local Postgres for development.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.