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.
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.
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.
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:
# 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.
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)
Please sign in to join the discussion.
No comments yet.
Be the first to share your perspective on this topic.