"Should we use DynamoDB or Postgres?" is probably the question I've answered most often in design reviews, and the honest answer almost always starts with "what does your access pattern look like?" rather than anything about the databases themselves. People reach for the database they know, then bend their application around it. I've watched teams force a relational schema into DynamoDB and others run a key-value workload on Aurora and pay for the privilege.

Here's the framework I actually use to choose between RDS, Aurora, and DynamoDB.

What each one is, briefly

  • RDS, managed relational databases (PostgreSQL, MySQL, MariaDB, SQL Server, Oracle). AWS handles patching, backups, and failover, but you still pick an instance class and manage storage scaling. It's the closest thing to "my familiar database, but managed."
  • Aurora, AWS's reimplementation of the MySQL and PostgreSQL engines with a distributed storage layer. Wire-compatible with those engines but with auto-scaling storage (up to 128 TB), faster replicas, and Serverless v2 that scales capacity in fine-grained increments.
  • DynamoDB, a fully managed key-value and document store. No servers, no instance sizing, single-digit-millisecond reads at any scale, but you must design around its access patterns rather than querying flexibly.

RDS vs Aurora: a narrower choice than it looks

These two are both relational, so the decision is about operational characteristics and cost, not data model. Aurora generally gives you better read scaling (up to 15 low-latency replicas sharing one storage volume), faster crash recovery, and storage that grows automatically. RDS is cheaper at the low end and supports engines Aurora doesn't (SQL Server, Oracle, MariaDB).

If you need PostgreSQL or MySQL and you're past a hobby project, Aurora is usually the better default, but Aurora Serverless v2 has a real minimum capacity floor, so a truly idle dev database can be cheaper on a small RDS instance you stop overnight.

The cost nuance that trips people up: Aurora bills storage I/O separately (unless you use the I/O-Optimized configuration), so an I/O-heavy workload can cost more than the instance price suggests. Run your workload and check the I/O line before assuming Aurora is cheaper.

The relational vs DynamoDB fork

This is the decision that actually matters, and it comes down to whether you know your access patterns up front.

Choose relational (RDS/Aurora) when…Choose DynamoDB when…
Ad-hoc queries, reporting, joins across entitiesAccess patterns are known and fixed at design time
Strong transactional consistency across many tablesYou need single-digit-ms latency at massive, spiky scale
Schema will evolve in unpredictable waysYou want zero server management and pay-per-request billing
Moderate, predictable throughputThroughput is unpredictable or bursts 100x

DynamoDB rewards you for designing the table around your queries, often a single-table design with composite keys and Global Secondary Indexes. It punishes you when a new query shows up that your key schema didn't anticipate, because there's no WHERE clause to fall back on; you either add a GSI or scan the whole table.

The same lookup in both worlds

Fetching a user's recent orders in Postgres is a join:

SELECT o.id, o.total, o.created_at
FROM orders o
WHERE o.user_id = $1
ORDER BY o.created_at DESC
LIMIT 20;

In DynamoDB you'd model orders under a partition key of the user and a sort key encoding time, so the "query" is a single key-condition lookup with no scan:

import boto3
from boto3.dynamodb.conditions import Key

table = boto3.resource("dynamodb").Table("commerce")

resp = table.query(
    KeyConditionExpression=Key("PK").eq("USER#a1b2")
        & Key("SK").begins_with("ORDER#"),
    ScanIndexForward=False,   # newest first
    Limit=20,
)
orders = resp["Items"]

The DynamoDB version is faster and scales flatter, but only because the data model was built for exactly this question. Ask a different question and you're rebuilding the table or adding a GSI.

A rough decision order

  1. Do you need flexible queries, joins, or cross-row transactions? → Relational.
  2. Relational on an engine AWS only offers via RDS (SQL Server/Oracle/MariaDB)? → RDS.
  3. Postgres/MySQL with serious read scaling or growth? → Aurora.
  4. Known access patterns, huge or spiky scale, want no servers? → DynamoDB.

Takeaways

  • The data model and access patterns decide the database, pick the store that fits the queries, not the one you know best.
  • RDS vs Aurora is an operations-and-cost choice within the relational world; watch Aurora's separate I/O billing.
  • DynamoDB is fastest and flattest at scale only when you design the keys around your queries; unanticipated queries are expensive.
  • It's fine to use more than one, relational for reporting, DynamoDB for a hot key-value path, rather than forcing everything into a single engine.