Quick verdict
Choose DynamoDB if your application runs on AWS, its access patterns are known and key-based (fetch a session, a cart, a user's recent events), and you want no instances to size or patch and per-request billing. Choose PostgreSQL if you need joins, aggregates, reporting or queries you have not thought of yet, foreign keys and constraints, transactions without small item caps, or the option to move between clouds. If you want PostgreSQL-style SQL with serverless operations on AWS, look at Aurora DSQL or Aurora Serverless, covered below, before choosing DynamoDB for that reason alone.
Amazon DynamoDB is a fully managed NoSQL service on AWS. You create tables, choose a primary key (a partition key, optionally with a sort key) and a capacity mode, and read items with GetItem, Query, Scan or PartiQL, a SQL-compatible language of which DynamoDB supports a subset. There is no engine version, instance or storage to manage, and on-demand capacity is the default mode. It runs only on AWS.
PostgreSQL is an open source object-relational database released under the PostgreSQL Licence, a permissive licence. The current release is 18.6. It offers standard SQL with joins, window functions, constraints and multi-version concurrency control, plus jsonb for semi-structured data. You can run it anywhere, or use a managed service such as Amazon RDS for PostgreSQL, Amazon Aurora PostgreSQL-Compatible Edition, or the equivalents on Azure and Google Cloud.
For DynamoDB against a document database, see DynamoDB vs MongoDB; for the broader trade-off, see SQL vs NoSQL.
Side by side
| Aspect | DynamoDB | PostgreSQL |
|---|---|---|
| What it is | Serverless AWS service; no instances or versions | Database software; self-managed or as a managed service |
| Licence and where it runs | Proprietary AWS service; AWS only (DynamoDB local for development) | PostgreSQL Licence (permissive); any platform or cloud |
| Data model | Items addressed by partition key and optional sort key; nested maps and lists | Tables, rows, constraints and foreign keys; jsonb for documents |
| Query language | Key-based API and a PartiQL subset; no joins or GROUP BY | Full SQL: joins, aggregates, window functions, CTEs |
| Design approach | Access patterns first; keys and indexes encode the queries | Normalised schema first; indexes added for new queries |
| Size limits | 400 KB per item | Up to 1 GB per field value (including jsonb) |
| Transactions | Up to 100 items and 4 MB; PartiQL transactions are all reads or all writes | Full ACID transactions with no item cap; Read Committed by default, Serializable available |
| Scaling | AWS manages partitioning and throughput | Vertical scaling and read replicas; write sharding is not built in |
| Pricing model | Per request (on-demand) or provisioned capacity, plus storage | Free software; managed services bill by instance or capacity unit, storage and I/O |
| Main trade-off | No database operations, but new questions can mean new indexes, new keys or scans | Ask anything in SQL, but you size and operate a server or cluster |
Key differences
Access-pattern-first design versus ad hoc queries
In DynamoDB you list the questions the application will ask before you design the table, then encode them in the partition key, sort key and secondary indexes. A read that names the partition key is efficient; one that does not is a scan. AWS's PartiQL documentation is explicit: a SELECT "can result in a full table scan if an equality or IN condition with a partition key is not provided in the WHERE clause", and it shows how to deny full-scan statements with an IAM policy.
-- DynamoDB (PartiQL): key-based read, served without a scan
SELECT OrderId, Total FROM "Orders" WHERE CustomerId = 'C123';
-- DynamoDB (PartiQL): filter on a non-key attribute = full table scan
SELECT OrderId, Total FROM "Orders" WHERE Total > 500;In PostgreSQL you model entities and relationships, then answer new questions with SQL and add indexes as needed. The same table serves the application's key lookups and an analyst's one-off report.
-- PostgreSQL 18: the application query
SELECT order_id, total
FROM orders
WHERE customer_id = 123
ORDER BY order_date DESC
LIMIT 10;
-- PostgreSQL 18: an ad hoc report on the same table
SELECT customer_id, SUM(total) AS revenue
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY customer_id
ORDER BY revenue DESC
LIMIT 5;DynamoDB's PartiQL has no equivalent of the second query: AWS lists the supported functions as SIZE, EXISTS, ATTRIBUTE_TYPE, BEGINS_WITH, CONTAINS and MISSING, and states that any SQL function not on the list is unsupported. Teams that need reporting on DynamoDB data usually export it to an analytics service.
PartiQL is SQL-like, not SQL
PartiQL makes DynamoDB look familiar, but each statement still maps to single-item or key-based operations. An UPDATE or DELETE must identify exactly one item by its full primary key; AWS states that "you can only update one item at a time". Changing many items takes a batch or a transaction of individual statements.
-- DynamoDB (PartiQL): updates one item, identified by its full key
UPDATE "Orders" SET Status = 'SHIPPED'
WHERE CustomerId = 'C123' AND OrderId = 'O-1001';-- PostgreSQL 18: one statement updates every matching row
UPDATE orders
SET status = 'SHIPPED'
WHERE customer_id = 123 AND status = 'PACKED'; Documents: DynamoDB items versus PostgreSQL JSONB
DynamoDB items are schemaless apart from the key attributes and can hold nested maps and lists up to the 400 KB item limit. PostgreSQL offers the same flexibility inside a relational table through jsonb, a decomposed binary format that the PostgreSQL documentation says is faster to process than json and supports indexing. A GIN index with the jsonb_path_ops operator class supports containment queries with @>.
-- PostgreSQL 18: relational columns plus a JSONB document
CREATE TABLE orders (
order_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL,
order_date timestamptz NOT NULL DEFAULT now(),
status text NOT NULL,
total numeric(10,2) NOT NULL,
attrs jsonb
);
CREATE INDEX orders_attrs_gin ON orders USING GIN (attrs jsonb_path_ops);
SELECT order_id, attrs ->> 'channel' AS channel
FROM orders
WHERE attrs @> '{"channel": "mobile"}';The result is that PostgreSQL can hold document-style data and still join it to relational tables and enforce constraints on the columns around it. DynamoDB has no joins, so related data is either duplicated into items or fetched with several requests.
Transactions and consistency
DynamoDB transactions (TransactWriteItems, TransactGetItems or PartiQL ExecuteTransaction) cover up to 100 actions and 4 MB within one account and Region. A PartiQL transaction must consist of either reads or writes, not both, with EXISTS allowed as a condition check. Reads are eventually consistent by default, and reads from global secondary indexes are always eventually consistent.
PostgreSQL transactions are ACID with no item cap: one transaction can update millions of rows, run DDL, and read its own writes. The default isolation level is Read Committed, and Repeatable Read and Serializable are available. Constraints, foreign keys and triggers are enforced inside the transaction, which is often the deciding factor for financial or inventory data.
Scaling and serverless relational options on AWS
DynamoDB partitions data and scales throughput for you; in on-demand mode there is no capacity to plan. A single PostgreSQL server scales vertically, adds read replicas for read traffic, and has no built-in sharding of writes across servers. That gap is the most common reason teams pick DynamoDB for very high write volumes.
AWS offers two ways to get serverless operations with a PostgreSQL interface. Amazon Aurora DSQL is a serverless, distributed relational service that AWS describes as PostgreSQL-compatible (currently with PostgreSQL 16), active-active across Availability Zones and, with peered clusters, across Regions in the same Region set. It has real differences from PostgreSQL: optimistic concurrency with retries on conflict, isolation fixed at Repeatable Read, up to 3,000 modified rows per transaction, one DDL statement per transaction, no temporary tables, triggers or PL/pgSQL, and no index support on json or jsonb columns. Aurora Serverless (formerly Aurora Serverless v2) is an autoscaling configuration of Aurora PostgreSQL or MySQL that adjusts capacity in 0.5 ACU steps and can scale to zero with automatic pause. For the wider choice between RDS and Aurora, see Amazon RDS vs Amazon Aurora; for distributed PostgreSQL-compatible databases outside AWS, see CockroachDB vs PostgreSQL.
Pricing and licensing
DynamoDB. Listed on the AWS DynamoDB on-demand pricing page in October 2026 for US East (N. Virginia), Standard table class, in USD: 0.625 per million write request units and 0.125 per million read request units, plus storage per GB-month. Provisioned capacity is billed per capacity unit per hour. Backups, streams, global tables and data transfer are charged separately, and rates vary by Region.
PostgreSQL. The software is free under the PostgreSQL Licence. Managed PostgreSQL is billed by the provider: Amazon RDS and Aurora provisioned clusters by instance hours, storage and (for some Aurora configurations) I/O, and Aurora Serverless by Aurora capacity units (ACUs) per second. Use the AWS Pricing Calculator for these.
Aurora DSQL. Listed on the Aurora DSQL pricing page in October 2026: billed by Distributed Processing Units (DPUs) for database activity and GB-months of storage, with DPU charges scaling to zero when idle. In US East (Ohio), 8 USD per million DPUs and 0.33 USD per GB-month; each month the first 100,000 DPUs and 1 GB of storage are free. We have not estimated workload costs; DPU use depends on the queries you run.
Pricing checked on the vendors' official pages on 7 October 2026. Prices change; confirm before buying.
Where each one leads
DynamoDB strengths
- Serverless: no instances, versions or patching, and on-demand capacity by default
- Partitioning and throughput scaling handled by AWS
- Per-request billing suits spiky or low-traffic workloads
- Predictable key-based access, with IAM policies that can block full-table-scan PartiQL statements
- Integrates with Lambda, Streams, IAM and global tables inside AWS
PostgreSQL strengths
- Full SQL with joins, aggregates and window functions for ad hoc questions
- Constraints, foreign keys and ACID transactions with no item cap
- JSONB with GIN indexes for document-style data in the same database
- Permissive PostgreSQL Licence; runs on any cloud or on premises
- Large ecosystem of managed services, extensions and tools
Limitations
DynamoDB limitations
- AWS only, so the data layer is tied to one provider
- No joins, GROUP BY or SQL aggregates; queries off the key become scans
- Each PartiQL UPDATE or DELETE affects one item
- 400 KB item limit and transactions capped at 100 items and 4 MB
- New access patterns can mean new indexes or remodelled keys
PostgreSQL limitations
- You size, patch and upgrade servers, or pay a managed service to
- Write scaling beyond one primary needs sharding or a distributed system
- Major version upgrades need planning
- Spiky, low-volume workloads can pay for idle capacity unless you use a serverless option
When to choose each
Choose DynamoDB if
- Your application is on AWS and access patterns are known and key-based
- You need very high or unpredictable throughput without managing servers
- Data per entity is small and self-contained, such as sessions, carts, device state or user profiles
- You want pay-per-request billing for a spiky or low-traffic service
Choose PostgreSQL if
- Your data is relational and you need joins, constraints and foreign keys
- Reporting or ad hoc analysis runs against the same data
- Requirements are still changing and you want to add queries without redesigning keys
- You need multi-row transactions larger than 100 items
- You want to avoid tying the database to one cloud
When neither is right
- You want SQL and PostgreSQL drivers with serverless operations on AWS: consider Aurora DSQL or Aurora Serverless, after checking DSQL's PostgreSQL compatibility limits; see Amazon RDS vs Amazon Aurora.
- You want a document database with richer queries than DynamoDB: see DynamoDB vs MongoDB and MongoDB vs PostgreSQL.
- You need a cache or session store in front of the database: see Redis vs MongoDB.
- Your workload is analytics over large volumes: a warehouse fits better; see data warehouse vs database.
Final recommendation
For most new applications whose questions are not fully known, PostgreSQL is the safer default in our view: SQL, constraints and JSONB cover relational and document needs in one place, and you can run it on any cloud. DynamoDB is the better fit when you are on AWS, the access patterns are known and key-based, and you value having no database to operate and capacity that AWS scales for you. If the attraction of DynamoDB is only "serverless", check Aurora Serverless or Aurora DSQL first, because they keep SQL; DSQL in particular has documented differences from PostgreSQL that you should test against your schema.
Frequently asked questions
Can I use SQL with DynamoDB?
Partly. DynamoDB supports a subset of PartiQL for SELECT, INSERT, UPDATE and DELETE. There are no joins or GROUP BY, the only functions are SIZE, EXISTS, ATTRIBUTE_TYPE, BEGINS_WITH, CONTAINS and MISSING, updates and deletes affect one item, and a SELECT without an equality or IN condition on the partition key scans the table.
Is PostgreSQL JSONB a replacement for DynamoDB?
For storing and querying documents, often yes: jsonb supports nested data, containment queries and GIN indexes, inside a database with SQL and transactions. It does not give you DynamoDB's serverless operations or automatic partitioning; for that on AWS, compare Aurora Serverless and Aurora DSQL.
Is Aurora DSQL the same as PostgreSQL?
No. AWS describes Aurora DSQL as PostgreSQL-compatible (currently with PostgreSQL 16), but it uses optimistic concurrency, fixes isolation at Repeatable Read, limits a transaction to 3,000 modified rows and one DDL statement, and does not support temporary tables, triggers or PL/pgSQL. Check its compatibility pages before migrating.
Which is cheaper, DynamoDB or PostgreSQL?
It depends on the workload. DynamoDB on-demand charges per request, which suits spiky or low traffic; managed PostgreSQL usually charges for instance or capacity time whether busy or not, unless you use Aurora Serverless with auto-pause. Model both in the AWS Pricing Calculator with your own request volumes.
Can DynamoDB do transactions like PostgreSQL?
DynamoDB has ACID transactions, but each covers up to 100 items and 4 MB in one account and Region, and a PartiQL transaction must be all reads or all writes. PostgreSQL transactions have no item cap and can include DDL.
Sources
- Amazon DynamoDB Developer Guide: PartiQL
- Amazon DynamoDB Developer Guide: PartiQL SELECT
- Amazon DynamoDB Developer Guide: PartiQL functions
- Amazon DynamoDB Developer Guide: PartiQL transactions
- Amazon DynamoDB Developer Guide: Constraints
- Amazon DynamoDB on-demand pricing
- Amazon Aurora DSQL User Guide
- Aurora DSQL: PostgreSQL compatibility considerations
- Aurora DSQL pricing
- Amazon Aurora User Guide: Aurora Serverless
- PostgreSQL documentation: JSON types
- PostgreSQL documentation: Transaction isolation
- PostgreSQL documentation: Limits
- PostgreSQL licence
- PostgreSQL versioning policy
Checked October 2026.
How we research comparisons: our editorial method.