PostgreSQL vs DynamoDB: When to Switch and How Much It Costs at Scale

Khanh Nguyen
Khanh Nguyen
(Updated: )
Listen to this article0 / 0
Minimalist line illustration comparing PostgreSQL and DynamoDB costs using a scale with a filing cabinet and money-weighted lockers.

Two engines, two totally different pricing logics — one bills for the server you reserve, the other bills for every request you make. Here's the real math behind both, and the point at which switching actually saves money.

A Table Row and a Key-Value Item Solve Different Problems

Think of PostgreSQL as a filing cabinet. Every folder (row) in a drawer (table) has the same tabs in the same order — name, date, amount — and folders in different drawers can reference each other, so pulling a customer's file automatically shows every order that customer ever placed. That cross-referencing is a JOIN, and it's the reason relational databases are still the default for orders, ledgers, and anything where the relationships between records matter as much as the records themselves.

DynamoDB works more like a wall of numbered lockers. Hand it a locker number — the partition key — and it opens that exact locker in roughly the same amount of time whether there are a hundred lockers or a hundred billion. It doesn't care what's inside two different lockers or whether they relate to each other; it just needs the number. That's why DynamoDB is fast for session storage, shopping carts, and IoT event streams, and awkward for anything that needs to ask "show me everything related to X" without already knowing X's exact key.

The cost story follows directly from that difference. A filing cabinet costs money whether or not anyone opens a drawer today — you're paying for the cabinet. A locker system that bills per open costs nothing when it's quiet and scales its bill with every single request when it's busy. That single distinction — pay for capacity vs. pay for requests — is what the rest of this article is really about.

How a Postgres row differs from a DynamoDB itemSide-by-side comparison of a PostgreSQL table row and a DynamoDB item, showing why one favors relationships and the other favors single-key speed.One Row vs. One ItemThe same customer order, stored two different waysPostgreSQL: a table rowDynamoDB: a table item• Fixed columns, every row the same shape• Foreign keys link to other tables• One query can JOIN across many tables• Strong fit: orders, ledgers, accounts• Flexible attributes, items can differ• No joins — one partition key, one lookup• Reads by key stay fast at any table size• Strong fit: sessions, carts, IoT eventsCost scales with instance sizeCost scales with request volumeVSStructure reference: PostgreSQL and DynamoDB official documentation

DynamoDB's Bill Can Swing 26x on One Setting

DynamoDB gives you a choice at table-creation time that most teams don't think hard enough about: on-demand or provisioned capacity. On-demand charges per read and write request — no planning required, and it scales instantly. Provisioned capacity has you commit to a throughput ceiling (read and write capacity units) and pay for that ceiling by the hour, whether you use it or not.

At AWS's current us-east-1 list prices, on-demand runs about $1.25 per million write-request units and $0.25 per million read-request units. Provisioned capacity runs roughly $0.00065 per write-capacity-unit-hour and $0.00013 per read-capacity-unit-hour. Do the conversion at full utilization and provisioned capacity comes out around 26 times cheaper per unit than on-demand — and a three-year reserved capacity commitment widens that gap even further. AWS cut on-demand throughput prices by half in late 2024, which narrowed the gap from what it used to be, but it didn't close it for steady, predictable traffic.

The catch is the word "predictable." Provisioned capacity only wins if your traffic doesn't spike far above what you've provisioned — unplanned bursts get throttled unless auto-scaling reacts in time, and auto-scaling watches metrics on a roughly one-minute cadence, not an instant one. On-demand is still the safer default for anything spiky, low-volume, or brand new. It's also worth knowing that every Global Secondary Index you add roughly doubles the write cost tied to that index, which is the single most common reason a DynamoDB bill comes in 2-3x over budget.

DynamoDB monthly cost: on-demand vs provisionedFor the same 100 million reads and 20 million writes per day, on-demand billing costs roughly 4.5 times more than provisioned capacity with auto-scaling.DynamoDB Monthly Cost: Two Billing ModesSame workload — 100M reads + 20M writes/day, us-east-1On-demand (per-request)≈$825/moProvisioned + auto-scaling≈$185/moSource: DynamoDB pricing analysis compiled July 2026, us-east-1 list prices

What a Production RDS PostgreSQL Instance Actually Costs

RDS PostgreSQL bills the opposite way: by the hour, for the server you've reserved, regardless of how many queries hit it. A dev-sized db.t4g.micro instance running Single-AZ lands around $12 a month — cheap enough that it's easy to underestimate what happens once real traffic shows up. A production-grade db.r6g.large (2 vCPUs, 16 GB RAM) running Multi-AZ for automatic failover — which AWS bills at exactly double the compute and storage of a single instance — lands closer to $437 a month once 500 GB of general-purpose storage is included.

That number moves a lot depending on one decision: reserved capacity. Commit to a three-year term on that same Multi-AZ db.r6g.large and the effective monthly cost drops to roughly $175 — a savings large enough that any workload expected to run more than about a year should be priced both ways before launch. pgRust's recent milestone of passing every PostgreSQL regression test as an AI-built Rust rewrite is a reminder that the engine itself keeps evolving, but the billing model underneath RDS — pay for the box, not the query — has stayed the same for over a decade.

What a production RDS PostgreSQL instance costsMonthly cost climbs from about twelve dollars for a dev-sized instance to several hundred dollars for a Multi-AZ production instance, dropping sharply with a three-year reserved commitment.RDS PostgreSQL: Instance Cost by Configurationdb.t4g.micro vs db.r6g.large (2 vCPU / 16GB), us-east-1 monthlydb.t4g.micro, Single-AZ (dev)≈$12/modb.r6g.large, Multi-AZ (on-demand)≈$437/modb.r6g.large, Multi-AZ (3-yr reserved)≈$175/moSource: AWS RDS instance pricing compiled July 2026, us-east-1, instance cost only

Test Both Engines Locally With Docker Before Committing a Cloud Bill

None of the pricing above matters if you're not sure which access pattern your application actually has. The cheapest way to find out is to run both engines locally, side by side, before any AWS bill starts accruing. A minimal sandbox looks like this:

YAML
# docker-compose.yml — local Postgres + DynamoDB Local, no AWS charges
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: local_dev_only
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  dynamodb-local:
    image: amazon/dynamodb-local:latest
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /data"
    ports:
      - "8000:8000"
    volumes:
      - dynamodata:/data

volumes:
  pgdata:
  dynamodata:

Point your application's data-access layer at both services in a staging branch, replay a sample of real traffic against each, and count two things: how many of your queries need a JOIN or a multi-row transaction (a point in favor of Postgres), and how many are simple key lookups your code already knows the ID for (a point in favor of DynamoDB). That count is more reliable than any pricing spreadsheet, because it tells you which engine your application actually needs before you price either one. It's also a useful gut check for teams leaning on AI coding agents to scaffold the data layer — production constraints like this access-pattern analysis are exactly where those agents tend to fall short without a human checking the assumption.

The Break-Even Line: When Switching Actually Pays Off

Put the two pricing models next to each other and a rough rule falls out. A reserved Multi-AZ PostgreSQL instance costs a fairly flat $175-$437 a month regardless of how many queries hit it, up to whatever that instance can physically handle. DynamoDB on-demand starts at effectively $0 and climbs with every request, while provisioned DynamoDB behaves more like PostgreSQL — a flatter, capacity-based bill — once traffic is steady enough to size it correctly.

That means the switch is rarely about data volume alone. It's about whether your access pattern is dominated by relationships (JOINs, multi-table transactions, ad-hoc reporting queries) or by high-volume single-key lookups. A workload with real relational structure will often cost less on a reserved PostgreSQL instance even at very high request volume, because the bill doesn't move with traffic. A workload that's pure key-value lookups, especially at spiky or unpredictable scale, tends to get cheaper on DynamoDB once it's out of the on-demand tier and onto provisioned capacity with auto-scaling tuned correctly.

A simple decision path for choosing between PostgreSQL and DynamoDBA flow diagram showing that transactional, relationship-heavy, or ad-hoc-query workloads point toward RDS PostgreSQL, while high-volume single-key lookups point toward DynamoDB.Which Engine Fits the Access Pattern?A starting question, not a full architecture reviewWhat is the dominant read/write patternfor this specific table?Joins, ad-hoc queries, ormulti-row transactionse.g. orders, ledgers, reportingSingle-key lookups at high,spiky, or unpredictable scalee.g. sessions, carts, IoT eventsStay on RDS or AuroraPostgreSQLDynamoDB is usuallycheaper at that scaleFramework only — always model your actual read/write ratio before migrating

None of this replaces modeling your actual workload. Item size, index count, region, and how much of your traffic is reads versus writes all move these numbers meaningfully. But the underlying mechanism doesn't change: PostgreSQL bills for the box, DynamoDB bills for the door you open, and the cheapest engine for a given table is whichever billing model matches how that specific table actually gets used.

Comments (0)

Sort by:

No comments yet.

Be the first to share your perspective on this topic.