Skip to content

MongoDB vs FerretDB vs DocumentDB: benchmarks across two runs

11 min readMongoDBFerretDBDocumentDBBenchmarks

In our MongoDB vs FerretDB benchmark, the biggest lesson was how much our own storage and PostgreSQL defaults mattered. That post compared MongoDB with stock and tuned FerretDB. This follow-up brings all three engines into one comparison: the earlier MongoDB and tuned FerretDB results, alongside a new FerretDB versus standalone DocumentDB run.

These tables span two benchmark runs, not a single three-engine test. MongoDB was measured in August and was not rerun in October. FerretDB appears twice so you can see the result from each run. Every performance column is dated, and the original article remains the source for the August measurements and their full configuration caveats.

In the October run, DocumentDB delivered 20-33% more throughput on pure reads, 24-35% more on the 95% read / 5% update mix, and 7-12% more on the 50/50 mix. FerretDB had lower p99 update latency in every 50/50 row. At 256 client threads, that was 931 ms for FerretDB versus 2,226 ms for DocumentDB.

Those are two useful results, and a throughput-only chart would hide the second one. This is one complete run per stack, not a repeated trial with confidence intervals.

What we compared in October

Here, DocumentDB means the open-source PostgreSQL-based project and its documentdb-local:pg17-1.0.0 package. It does not mean Amazon DocumentDB, and neither Amazon DocumentDB nor MongoDB Atlas was tested.

FerretDB also uses PostgreSQL with a DocumentDB extension. The FerretDB project describes that stack; the DocumentDB project provides its own MongoDB protocol gateway. We compared the two packaged paths a client can connect to, not PostgreSQL against a wholly unrelated storage engine.

ComponentFerretDB armStandalone DocumentDB arm
Frontend packageghcr.io/ferretdb/ferretdb:2, digest pinnedghcr.io/documentdb/documentdb/documentdb-local:pg17-1.0.0, digest pinned
PostgreSQL packageFerretDB's postgres-documentdb:17, digest pinnedBundled in the DocumentDB Local image
Recorded PostgreSQL version17.617.11
Storage profile16K ZFS records, 2 GiB ARC capSame
PostgreSQL memory2 GiB shared buffersSame
Commit settingssynchronous_commit=on, full_page_writes=onSame

The different PostgreSQL minor versions and bundled extension builds matter. These results compare whole stacks. They do not isolate gateway overhead or prove that replacing one frontend alone produces the measured difference. Full image digests and captured settings are in the measurement record.

The setup

We used two separate OVH d2-8 instances in US-EAST-VA-1 on October 23, 2026. Each had 4 vCPUs, 8 GB RAM, Debian 13, and a 100 GB network-attached high-speed volume. Each ran one database stack and its own YCSB client. Client CPU and memory therefore shared the machine with the database on both arms.

This follows the hardware profile and workload structure of the earlier article. It measures this small dedicated-instance profile, not shared-plan performance or every possible deployment of either engine.

  • YCSB commit 66302f301b13f60d4bcb2f29f478586bb1d6f2e0, Java 17.0.20.
  • 10 million records loaded per engine, then 1 million operations per phase.
  • Workloads C (100% read), B (95% read / 5% update), and A (50% read / 50% update).
  • 16, 64, and 256 client threads; 32 threads for the initial load.
  • Write concern w=1; a 600-second cap per workload phase.
  • PostgreSQL max_wal_size=8GB, effective_io_concurrency=200, random_page_cost=1.1, and max_connections=300 on both arms.

The dataset is roughly 10 GB of logical field data, larger than machine RAM. Physical storage size, compression, and the pages actually accessed are different quantities; this does not prove that every request had to go to disk.

The suite ran C, then B, then A, with thread counts ascending within each workload. It loaded once per engine and reused the data across phases. It did not reset caches or randomize phase order. The same client pool behavior as the earlier article applies: the default MongoDB driver pool is 100, so 256 client threads does not mean 256 simultaneous database connections.

All three engines, with each run labeled

The August columns reproduce the published MongoDB 8.0.29 and tuned-default FerretDB 2.7 results from our earlier benchmark. They exclude its stock FerretDB and relaxed-durability variants. MongoDB ran on August 18 and the tuned FerretDB arm on August 19, on separate boxes of the same profile. The October columns come from the new October 23 run described above.

All columns use the 4 vCPU, 8 GB hardware profile and the same workload shapes. Matching those does not eliminate differences in machines, dates, software packages, caches, or storage performance. Compare the August pair and the October pair within their documented conditions; do not read an October-to-August ratio as a measured engine speedup.

Throughput is rounded to whole operations per second. For the October run, all 18 workload phases completed their full million operations before the time cap; the YCSB summary counters reported successful returns for all reads and updates. Both initial loads reported 10 million successful inserts. This is a client return-status check, not an independent audit of every stored value.

Workload C: 100% read

Client threadsMongoDB (Aug 2026), ops/secFerretDB tuned (Aug 2026), ops/secFerretDB tuned (Oct 2026), ops/secDocumentDB (Oct 2026), ops/sec
167553,8884,2655,122
647564,0654,3425,338
256 (pool-limited)7533,8514,1005,462

Workload B: 95% read / 5% update

Client threadsMongoDB (Aug 2026), ops/secFerretDB tuned (Aug 2026), ops/secFerretDB tuned (Oct 2026), ops/secDocumentDB (Oct 2026), ops/sec
167003,6113,7924,711
647503,9193,8415,196
256 (pool-limited)7123,5263,8385,189

Workload A: 50% read / 50% update

Client threadsMongoDB (Aug 2026), ops/secFerretDB tuned (Aug 2026), ops/secFerretDB tuned (Oct 2026), ops/secDocumentDB (Oct 2026), ops/sec
164911,6311,8862,016
644641,7521,7832,005
256 (pool-limited)4651,5861,7081,821

DocumentDB led FerretDB throughput in all nine October matched rows. In the historical August comparison, tuned FerretDB led MongoDB across the same workload shapes. The original article describes the MongoDB arm as I/O-bound on that small-memory setup; that result should not be generalized to a larger-memory deployment. In October, DocumentDB's advantage over FerretDB was largest on the read-heavy mixes and narrowed on the 50/50 mix. Neither October stack gained mixed-workload throughput by moving from 16 to 256 client threads.

Update p99 latency: the part throughput misses

Lower is better. October values are milliseconds, converted from YCSB's microsecond summaries and rounded to one decimal place. August values retain the whole-millisecond precision published in the original latency table. That table reported only the 50/50 mix; a dash means the historical value is not included in that source table, not zero.

WorkloadClient threadsMongoDB (Aug), msFerretDB tuned (Aug), msFerretDB tuned (Oct), msDocumentDB (Oct), ms
95/516--102.096.2
95/564--156.9306.9
95/5256--263.4315.4
50/501615610092.1103.8
50/5064664388324.1687.1
50/502561,682849931.32,226.2

At 64 threads on the 50/50 mix, DocumentDB delivered about 12% more throughput, but its update p99 was 687 ms versus FerretDB's 324 ms. At 256 threads, DocumentDB's throughput lead was about 7%, while update p99 was 2.23 seconds versus 0.93 seconds.

That is why an application with a latency budget should look beyond the operations-per-second column. Higher aggregate throughput did not mean better slow-request behavior in this run. These are observed tails, not a diagnosis of their cause: we did not collect enough profiling evidence to attribute them to checkpoints, gateway scheduling, or another component.

How this relates to the MongoDB comparison

Read the earlier MongoDB vs FerretDB benchmark alongside this one. It contains the MongoDB measurements, the stock-versus-tuned FerretDB comparison, and the storage/memory ablation that led to the profile used here.

The two FerretDB columns are useful context: even the tuned profile produced different numbers between August and October. They are a visible reminder that hardware class and configuration labels do not make separate runs interchangeable. We have shown the historical MongoDB results so readers can compare all three engines, while keeping each result attached to the experiment that produced it. A fresh three-engine run would be needed to establish a current matched ranking.

The useful comparison is:

QuestionEvidence
How did MongoDB and FerretDB compare on the earlier small-instance setup?The earlier article's measured tables and caveats
How did the two PostgreSQL-backed document stacks compare on October 23?This article's matched FerretDB and standalone DocumentDB run
Which of all three wins today on my application?Not answered by these two runs; requires a fresh matched test and application compatibility checks

What this run does not settle

One run per configuration gives no estimate of run-to-run variance. Separate VMs and volumes can vary despite matching specifications. The PostgreSQL and extension builds are not identical. The client runs on the database machine, and the fixed workload order carries cache and update history into later phases.

YCSB's point reads and updates also do not test aggregation pipelines, application-specific indexes, transactions, change streams, failover, replication, or full MongoDB compatibility. w=1 with synchronous PostgreSQL commits enabled is the tested configuration, not a replicated durability or crash-recovery guarantee. Neither stack was tested with the earlier article's relaxed-durability opt-in.

For a read-heavy workload on this hardware, DocumentDB's throughput result is a reason to evaluate it. For a mixed workload with a strict slow-update budget, FerretDB's lower measured p99 is equally relevant. Neither result is enough on its own to justify migrating a production application.

Download the measurements

The CSV contains the October run only: all nine workload rows and the load phase for each of its two engines, including operation counts and p99 latency. The JSON measurement record contains the parsed YCSB summary counters, image pins, PostgreSQL settings, and SHA-256 hashes of the retained source archives. Raw archives are kept privately because setup and connection logs contain credentials; the downloads expose only metrics and non-secret configuration.

Both suites completed, the logs were saved, and deletion of both temporary instances and both attached volumes was confirmed. The measurements, not the temporary servers, are what we kept.

Try the MongoDB-compatible workflow on Layerbase

We are big fans of both DocumentDB and FerretDB. Both projects give developers valuable ways to work with documents through the MongoDB-compatible protocol on PostgreSQL, and we want both to succeed. Publishing their strengths and tradeoffs helps you choose the right fit for your application.

Layerbase Cloud lets you use that MongoDB-compatible workflow with database branching: create an isolated branch for development, a migration rehearsal, or a CI run. Bring your existing driver and test the commands your application relies on; protocol compatibility does not mean every MongoDB feature behaves identically.

You can get that workflow at a fraction of the price of a dedicated Atlas M30 deployment. For a concrete example, our Dedicated 8GB preset is $65/month, while MongoDB's own billing example prices an AWS M30 at $0.54/hour, about $394 over 730 hours. That puts our preset at roughly one-sixth of that 730-hour estimate. Prices checked October 23, 2026; Atlas pricing varies by region and configuration. This is a single-node Layerbase preset with backups and branching versus an Atlas three-node replica set, so the availability architectures differ. It is not a claim about every Atlas tier, and we did not benchmark Atlas.

If you want to run MongoDB itself locally, you can use the Layerbase CLI or Layerbase Desktop. We do not host MongoDB itself in Layerbase Cloud; our cloud MongoDB-compatible options are FerretDB and DocumentDB.

Create a database on Layerbase Cloud, try your application's queries, and branch it before your next migration. For the background behind these results, start with our original MongoDB vs FerretDB benchmark.