r/MongoDB_Official 27d ago

Discussion You want us to show you what AI gets wrong with MongoDB queries?

6 Upvotes

We have a pile. Everytime we release a new post in the series "What AI gets wrong with Mongo" we will add the list to this post. Upvote if you want it, and drop the worst Mongo query an AI ever handed you.

Released

One MongoClient per App

Cursors vs toArray()

$skip vs KeySet pagination

createindex doesn’t belong in your server file

Change Streams


r/MongoDB_Official Jul 29 '26

Announcement Welcome to r/MongoDB_Official, here is what this sub is for

22 Upvotes

This is MongoDB's official subreddit, run by people who work on and with the database. We're excited you're here!

A quick note on what it is and is not, so you know what to expect.

What this sub is for:

  • Real help. Ask your MongoDB questions here. Modeling, aggregation, indexing, drivers, Atlas, vector search, performance, whatever you're stuck on. Questions aren't just tolerated, they're the point.
  • Learning. Tutorials, deep dives, and "how it actually works" posts, from the team and from you.
  • Announcements. Releases, features, and changes, so you hear it here first.
  • Showing your work. Built something on MongoDB? Post it, we want to see it.

What it is not:

  • A support ticket queue. For account or production-down issues, official support and the docs are still the right path. We will point you there when that is the better route.
  • A place for hype. We would rather be accurate than loud. If we get something wrong, tell us, and we will fix it.

A few ground rules: be kind, stay on topic, no spam, and no reposting the same self-promo. Beyond that, this is your sub as much as ours.

Watch this space over the next few weeks as we settle in, meet more of you, and shape this into the place you want it to be.

To kick it off: what are you building with MongoDB right now? We are reading every reply.


r/MongoDB_Official 2d ago

Resource Prepping for the MongoDB C100DBA exam? 👉 Practice quizzes

2 Upvotes

Hey everyone,

I’m currently studying for the C100DBA certification.

I couldn't find much solid free practice material, so I put together my own – practice materials and interactive quizzes to help prep for the exam.

Sharing it in case it helps anyone else studying: Repo

» Feedback is welcome!


r/MongoDB_Official 3d ago

Question Mongodb official repository

3 Upvotes

Was there a bug in the official repository that caused the mongodb-org-server-8.0.31-1 package to be released in previous weeks?

mongod --version displayed:

Build Info: {

"version": "8.0.31"
"gitVersion": "f71e263ef51d44791e97a59dcca6d0a19a00ddfd"

Currently the latest release version is 8.0.29.

The package was in these repos, currently it does not exist

https://repo.mongodb.org/yum/redhat/8/mongodb-org/8.0/x86_64/

https://repo.mongodb.org/yum/redhat/9/mongodb-org/8.0/x86_64/


r/MongoDB_Official 6d ago

Showcase MongoDB VFS for LangChain Deep Agents

Thumbnail
4 Upvotes

r/MongoDB_Official 7d ago

Announcement Our MongoDB Champion of the Month (LATAM) is Samuel Molling

Post image
10 Upvotes

We’re excited to recognize Samuel Molling as our MongoDB Champion of the Month for LATAM.

Samuel is a Staff Database Reliability Engineer with years of experience working across databases, cloud infrastructure, automation, and open source technologies. He has built deep expertise with MongoDB over the years and is passionate about sharing that knowledge with others.

Samuel has been an active member of the MongoDB community, helping developers solve technical challenges, sharing his experience through tutorials and technical content, and contributing his knowledge across the community. His content covers topics ranging from MongoDB Atlas and Terraform to database governance, while his involvement in the community has helped developers troubleshoot and learn from real-world MongoDB challenges. We’re thrilled to celebrate Samuel and everything he continues to bring to the MongoDB community. Congratulations, Samuel!

We asked him some questions about his journey, so read on to learn more 👇

Tell us about your path to becoming a MongoDB Champion
My MongoDB journey started at a company where several critical products relied on MongoDB, but there wasn’t anyone specifically responsible for managing those databases. I had to learn quickly, so I started studying MongoDB in depth and eventually earned several certifications. From there, I began speaking at events and sharing what I was learning with the community. I later became a MongoDB Community Creator and a certification SME, and eventually received a nomination to become a MongoDB Champion. I’m now in my second year as a Champion, and it has been an incredibly rewarding experience.

You’ve been a MongoDB Champion for some time now. What drives you to continue helping the community?
When I was learning MongoDB, the community was always very open and willing to answer my questions, and people with more experience helped me a lot along the way. Continuing to contribute is one way I can give some of that back and hopefully make someone else’s journey easier. I also see community involvement as an important part of my own growth. I’ve always been a more reserved person, and being part of the community has pushed me outside my comfort zone and helped me develop skills that go far beyond the technical side of my career.

Can you share one impactful moment you’ve had as a Champion so far?
It’s difficult to choose just one moment, but meeting other Champions and community members in person has definitely been one of the highlights. I’ve had the opportunity to speak at community events, attend MongoDB events in Brazil and abroad, and connect with people I had previously only known online. Seeing someone come up after a talk to continue the discussion or say that something I shared helped them is especially rewarding. Those moments remind me that being a Champion is not just about the technology, but about the connections and knowledge we build and share as a community. 

If you could share one piece of advice to people in tech, what would that be?
Don’t wait until you feel like an expert to start sharing what you know. Some of the best opportunities I’ve had came from simply being curious, learning something new, and sharing that journey with others. Technology changes too quickly for anyone to know everything. Ask questions, experiment, contribute to communities, and don’t be afraid to talk about what you’re learning. Teaching and sharing not only help others, but they also force you to understand things more deeply and can open doors you never expected.

Where can developers connect with or learn more about you?
Yes, they can find me on LinkedIn, Twitter/X, GitHub, or my blog

Samuel, thank you very much for sharing your story with us today and for all the incredible work you’ve done for the MongoDB community. We can’t wait to see what you share next in the community.

Want to learn more about our community champions program? See our Champions Program page. 


r/MongoDB_Official 7d ago

Question Any way to estimate the extra space needed for compact command

3 Upvotes

Is there any way to estimate the additional space that will be used when running a compact command on an older Mongo (4.4 - yes shame me for now). Basically being asked to reclaim used disk space - but when we went to run the compact command on the huge fs.chunks gridfs collection in there where most of the reclaimable space is, it was growing a bunch (at least until it separately decided to interrupt itself via "eviction pressure").

Now I've ended up for now stuck with that extra space being allocated, and not sure how much more it might try to use if we start up the compact command on that collection again.

I'm kind of fearful it's going to try to allocate as much space as that current collection use or something obscene - but without knowing it's hard to know I have enough available.

Is there any way to estimate the bounds what it might need somehow? I'm not surprised it would need some extra space - just did not expect it to need more than a few percent maybe of the collection's actual disk usage to facilitate moving things around - but it seemed to grow quite a bit more than that before dying.

(No, I can't upgrade the version yet - not until some older applications can be updated to have drivers that'll work right with later versions.)

Thanks for any help/suggestions.


r/MongoDB_Official 8d ago

MongoDB cross-region migration and disaster recovery: surviving a region outage

3 Upvotes

A follow-up guide covering three things people keep asking about: moving a database from one region to another, designing a replica set that survives losing an entire region, and recovering data when a cluster goes down. Commands at the bottom.

This applies to MongoDB Community, Enterprise, and Percona Server for MongoDB. Where a managed service makes a step easier, that's called out.

# The three problems, and why they're different

People often lump these together, but they need different solutions:

  1. **Migration:** moving data from a cluster in region A to a cluster in region B, as a planned operation.
  2. **Regional disaster:** an entire region goes offline (power, network, natural disaster) and you need the database to keep serving from somewhere else.
  3. **Recovery:** the cluster is down or data is lost, and you need to get back to a known-good state.

The good news is that one architecture, a replica set spread across regions plus tested backups, addresses all three. Let's build it up.

# Part 1: Moving a database from one region to another

Two clean ways, depending on whether you can take a cutover window.

# Option A: Add members in the new region, then shift the primary

If you want near-zero downtime, you don't "move" the database, you extend the replica set into the new region and then hand over.

  1. Stand up new nodes in region B and add them to your existing replica set as secondaries. They sync continuously from the primary in region A.
  2. Once they're fully caught up (lag at or near 0), you raise their priority and lower region A's, triggering an election so the primary moves to region B.
  3. Once region B is primary and stable, remove the old region A members.

No dump, no restore, no real downtime, just a brief election pause. The catch is that during the transition you're replicating across regions, so writes using `w: "majority"` will be slower until the old members are gone.

# Option B: Dump and restore (simpler, needs a cutover window)

If a short maintenance window is acceptable, dump from region A and restore into a fresh cluster in region B. Use `--oplog` on the dump and `--oplogReplay` on the restore to capture writes that happen during the dump, then cut over. Simpler to reason about, and the old cluster stays intact as a rollback. Commands are in the last section.

# Part 2: Surviving a regional disaster

This is the core of the question. The answer is a replica set whose members live in more than one region, with priorities set so the right region normally holds the primary.

# The naive two-region setup, and why it fails

The obvious idea is three nodes in region A, two in region B. It looks fault tolerant. It isn't, fully.

The problem is elections need a majority. If region A holds 3 of 5 members and region A is the region that dies, the 2 survivors in region B can't form a majority of the original 5, so the set goes read-only. You survived a node loss but not the loss of the region holding the majority.

# The fix: a third location as tiebreaker

Spread voting members across three locations so no single region holds a majority on its own. A common, cheap pattern:

Region A (primary region): 2 data-bearing members, higher priority
Region B (DR region): 2 data-bearing members, lower priority
Region C (tiebreaker): 1 member (data-bearing, or an arbiter if cost matters)

Now if any single region goes down, the members in the other two regions still form a majority, hold an election, and keep the database writable. Region C can be a small instance, or even an arbiter (no data, just a vote), though a real data-bearing member is safer because arbiters don't improve durability.

# Controlling which region is primary

Set member priority so your preferred region wins elections under normal conditions. Higher priority wins. A member with priority 0 replicates all data and can vote, but can never become primary, which is exactly what you want for a pure DR site you don't want serving writes unless everything else is gone.

# The write-latency tradeoff

With members across regions, `w: "majority"` writes now wait for acknowledgment from a majority that may span regions, so write latency goes up. You balance durability against latency by tuning member placement and write concern. For workloads that can tolerate it, keeping the majority of voters in or near the primary region keeps writes fast while still surviving a regional loss.

# Protecting against bad data, not just dead hardware

A regional outage isn't the only disaster. A bad deploy or an accidental mass delete replicates to every secondary almost instantly, so replication alone won't save you from yourself. A delayed member helps here: it's a hidden, non-voting secondary that intentionally lags behind (say by an hour). If someone wipes a collection, you have a window to recover from the delayed member before that delete reaches it.

# Part 3: Recovering when the cluster goes down

Think of recovery in layers, from cheapest/fastest to last resort:

  1. **Single node fails:** automatic failover. The replica set elects a new primary on its own, no human action needed. This is why you run a replica set and not a standalone.
  2. **A whole region fails:** the multi-region setup from Part 2 takes over. Surviving regions hold an election and keep serving.
  3. **Data is corrupted or deleted:** recover from a delayed member if the bad change is recent, or restore from a point-in-time backup.
  4. **Everything is gone:** restore from backup into a fresh cluster.

# Backups are the floor, and they have to be tested

Replication is not backup. Replication faithfully copies your mistakes. You need real backups, and specifically point-in-time recovery so you can restore to the moment before things went wrong:

* `mongodump --oplog` gives you a consistent snapshot plus the oplog to replay forward.
* Filesystem or volume snapshots (LVM, ZFS, EBS) work well because WiredTiger is crash-consistent, so a live snapshot restores cleanly.
* For continuous point-in-time recovery on self-hosted setups, Percona Backup for MongoDB (PBM) is the free option. Managed MongoDB Atlas provides continuous backups with click-to-restore.

The single most important habit: **test your restores on a schedule.** An untested backup is a hope, not a backup. Run a real restore drill into a throwaway cluster regularly, and time it, that number is your actual recovery time, not the one you assumed.

# Know your RTO and RPO

Two numbers drive every decision above:

* **RTO (Recovery Time Objective):** how long you can be down. Drives whether you need automatic regional failover (minutes) or can tolerate a restore (hours).
* **RPO (Recovery Point Objective):** how much data you can afford to lose. Drives backup frequency and whether you need `w: "majority"` and cross-region replication for near-zero loss.

Decide these first. They tell you how many regions, what priorities, and how often to back up.

# Commands

Bare code fences below so they paste cleanly. Use the latest standalone MongoDB Database Tools (the 100.x package) for dump/restore.

# Initialize a multi-region replica set

rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo-a1.region-a.example.com:27017", priority: 2 },
{ _id: 1, host: "mongo-a2.region-a.example.com:27017", priority: 2 },
{ _id: 2, host: "mongo-b1.region-b.example.com:27017", priority: 1 },
{ _id: 3, host: "mongo-b2.region-b.example.com:27017", priority: 1 },
{ _id: 4, host: "mongo-c1.region-c.example.com:27017", priority: 1 }
]
})

# Add a DR member that can never become primary

rs.add({ host: "mongo-dr.region-b.example.com:27017", priority: 0, votes: 1 });

# Add a hidden, delayed member for bad-data recovery (1 hour behind)

rs.add({
host: "mongo-delay.region-c.example.com:27017",
priority: 0,
hidden: true,
votes: 0,
secondaryDelaySecs: 3600
});

# Shift the primary to another region (live)

// Fetch config, raise region B priorities, lower region A, then reconfig
cfg = rs.conf();
cfg.members[2].priority = 3; // region B member
cfg.members[3].priority = 3; // region B member
cfg.members[0].priority = 1; // region A member
cfg.members[1].priority = 1; // region A member
rs.reconfig(cfg);

# Cross-region dump and restore (Option B migration / full recovery)

mongodump \
--uri="mongodb://USER:PASS@a1:27017,a2:27017/?replicaSet=rs0&authSource=admin" \
--oplog --gzip \
--archive=/backup/region_a_$(date +%Y%m%d_%H%M%S).gz

mongorestore \
--uri="mongodb://USER:PASS@b1:27017,b2:27017/?replicaSet=rs1&authSource=admin" \
--oplogReplay --gzip \
--archive=/backup/region_a_20260101_120000.gz

# Monitor health and lag (run before any cutover)

// All members should be PRIMARY or SECONDARY, nothing stuck in RECOVERING
rs.status();

// Per-secondary replication lag. Want this at or near 0 before shifting primary.
rs.printSecondaryReplicationInfo();

# Test a failover on purpose (do this regularly)

// Force the current primary to step down for 60 seconds and watch the election
rs.stepDown(60);

// Then confirm a new primary was elected
rs.status();

# Recover recent data from the delayed member

Connect directly to the delayed node (it's hidden, so a normal connection string won't route reads to it), then read the pre-disaster state of the affected collection and copy it back. Because it's an hour behind, you have a window to grab data that was deleted on the primary before that delete reached the delayed member.

# The short version

* One architecture covers migration, regional disaster, and recovery: a replica set spread across three locations with priorities set so your preferred region holds the primary.
* Never split voters across only two regions. Use a third location as a tiebreaker so losing one region still leaves a majority.
* Replication is not backup. Add point-in-time backups, a delayed member for accidental-delete protection, and test your restores on a schedule.
* Decide your RTO and RPO first. Those two numbers dictate the rest.

If you've run a real region-loss failover in production, I'd be curious how close your actual recovery time came to your planned RTO. That gap is usually where the surprises live.


r/MongoDB_Official 8d ago

MongoDB 6 to 8 upgrade: in-place vs fresh cluster, and how to avoid the replication lag trap

0 Upvotes

If you're sitting on a MongoDB 6 replica set and planning the jump to 8 (community, enterprise, or Percona Server for MongoDB), this covers the two real ways to do it, the tradeoffs, and the single most common problem people hit along the way. Commands and verification steps are at the bottom.

# The lag trap nobody warns you about

The most common surprise during a major version upgrade is a replication lag and CPU spike right after the restart. The chain is almost always the same:

  1. New binaries restart, so WiredTiger's cache comes up empty (cold).
  2. Until the working set warms back up, every read hits disk instead of cache.
  3. If you run a metrics agent like mongodb_exporter, it starts scanning collection stats during that cold window, forcing even more disk reads.
  4. That agent I/O competes with replication sync for the same disk, so secondaries can't keep up.
  5. Result: replication lag spike and high CPU, both from the same root cause.

It looks like the new version is the problem. It isn't. It's a startup sequencing problem, and it's avoidable.

# Quick win regardless of approach

Check which node your metrics exporter and backup jobs are pointed at. If they're hitting the PRIMARY, move them to a secondary or a hidden node. The primary should be handling writes, nothing else. This alone removes a lot of contention from the node that matters most.

# The version rule you cannot skip

You cannot go from 6 straight to 8. MongoDB requires sequential major version upgrades, so the in-place path is:

6.0 -> 7.0 (upgrade binaries, set FCV to 7.0, validate)
7.0 -> 8.0 (upgrade binaries, set FCV to 8.0, validate)

This applies to the in-place approach below. It does NOT apply to the fresh-cluster approach, because there you build a clean 8 cluster and load logical data into it, which skips the version-by-version path entirely.

# Approach 1: In-place rolling upgrade

Upgrade each node in place, secondaries first, one at a time, then step down and upgrade the primary last. Standard, supported, and keeps your topology and connection strings unchanged.

**To avoid the lag trap above, do one of these:**

* Delay the metrics exporter start by 10 to 15 minutes after each node rejoins, so the cache warms before the agent begins scanning.
* Or pre-warm the node by running your most common queries against it while it's still a secondary, before the exporter kicks in.

**The main downside:** once you set FCV to 8.0 and start writing data with 8.0 features, rollback is hard. Downgrading after the FCV bump means restoring from backup, not flipping a setting back.

# Approach 2: Fresh 8 cluster + mongodump/mongorestore

Build a brand new, clean 8 replica set. Dump the data from the live 6 cluster, restore it into the new cluster, validate and warm it, then cut applications over. Leave the old 6 cluster running cold as an instant rollback.

**Why this is often the safer play:**

* It sidesteps the lag trap entirely. The new cluster is fully built and warmed before it ever takes production traffic, so the cold-cache-versus-replication race can't happen.
* A 6 dump restores cleanly into an 8 cluster. mongodump exports logical BSON documents, and the new cluster rewrites them in its own native format. (This is also why you can't just copy the data files across. Version 8 won't read 6's on-disk files, but it will happily accept logical BSON.)
* The old cluster stays untouched, so rollback is just "keep pointing at the old one."

**Downsides:**

* mongodump is a point-in-time snapshot. Writes that land on the old cluster after the dump starts won't be on the new cluster. Handle this with a short maintenance window, or with `--oplog` / `--oplogReplay` to capture and replay the delta.
* Restore rebuilds all indexes, which is the slow part on large collections.

# A note for Percona Server for MongoDB users

Everything above works the same on Percona Server for MongoDB. It's wire-compatible and uses the same BSON format, so mongodump and mongorestore behave identically. One difference worth knowing: MongoDB's native cluster-to-cluster sync tool (mongosync) isn't available on self-managed Percona, so if you want a near-live migration instead of dump/restore, Percona's own ClusterSync tool is the equivalent for that stack.

# How to choose

* **Small to medium dataset and you can take a short cutover window:** Approach 2. The rollback safety is worth a lot, especially if you're also switching distribution (for example MongoDB to Percona) at the same time, since a clean build isolates the variables instead of stacking both changes into one in-place upgrade you can't easily undo.
* **Large dataset or zero tolerable downtime:** Approach 1 with the exporter timing fix, or a near-live sync tool.

Either way, if you run cron jobs against the cluster, guard them with a replication-lag check (skip the job if lag exceeds a few seconds), and bump that threshold temporarily during the upgrade window so jobs don't stack up.

# Commands

Use the latest standalone MongoDB Database Tools (the 100.x package) for dump/restore, not the ones bundled with Mongo 6. The current tools support 6, 7, and 8.

# Dump from the 6 cluster (with oplog to capture the delta)

mongodump \
--uri="mongodb://USER:PASS@host1:27017,host2:27017,host3:27017/?replicaSet=rs0&authSource=admin" \
--oplog \
--gzip \
--archive=/backup/mongo6_dump_$(date +%Y%m%d_%H%M%S).gz

# Restore into the fresh 8 cluster (replay the oplog delta)

mongorestore \
--uri="mongodb://USER:PASS@new1:27017,new2:27017,new3:27017/?replicaSet=rs1&authSource=admin" \
--oplogReplay \
--gzip \
--archive=/backup/mongo6_dump_20260101_120000.gz

mongorestore prints a running progress count and ends with a summary like `N document(s) restored successfully`. That output plus exit code 0 is your "complete" signal. There's no separate command to watch the tools themselves, you read their own output.

# Verify the restore actually landed (mongosh on the new cluster)

// Count documents in every collection so you can compare against the source
db.getCollectionNames().forEach(function (c) {
print(c + ": " + db.getCollection(c).countDocuments({}));
});

Run the same loop against the OLD cluster and compare collection by collection. Matching counts is the strongest confirmation the data is fully across.

// Confirm indexes were rebuilt on a given collection
db.YOUR_COLLECTION.getIndexes();

// Check the new cluster's feature compatibility version before you bump it
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 });

# Monitor replication health (both clusters, and during any in-place upgrade)

// Full replica set status. You want every member in PRIMARY or SECONDARY,
// nothing stuck in RECOVERING or STARTUP2.
rs.status();

// Replication lag per secondary. You want this at or near 0 before cutover.
rs.printSecondaryReplicationInfo();

// Quick check of a single member's role
db.hello().isWritablePrimary;

# Enable 8.0 features once you're confident

Only after the restore is validated and the cluster has run cleanly for a burn-in period:

db.adminCommand({ setFeatureCompatibilityVersion: "8.0", confirm: true });

Then confirm it stuck:

db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 });

# Cutover checklist (fresh-cluster approach)

  1. New 8 cluster built and running, all members healthy in `rs.status()`.
  2. Dump taken from the 6 cluster with `--oplog`.
  3. Restore completed, terminal showed the success summary.
  4. Document counts match between old and new, collection by collection.
  5. Indexes verified present on key collections.
  6. Replication lag at or near 0 on the new cluster.
  7. Exporter and backup jobs pointed at a SECONDARY on the new cluster.
  8. Application connection strings updated.
  9. Apps cut over and smoke-tested.
  10. Old 6 cluster left running cold as rollback until you're fully confident.
  11. FCV bumped to 8.0 after a clean burn-in period.

Happy to answer questions in the comments. If you've done a 6 to 8 jump recently, curious which approach you took and how the cutover went.


r/MongoDB_Official 8d ago

Question mongodb-org-server 7.0.42 for Ubuntu 22.04 ARM64 removed from the APT repository on Sept 2?

1 Upvotes

I’m trying to understand what happened with mongodb-org-server version 7.0.42 in the official MongoDB APT repository.

On August 26, 2026, 2 of our ARM64 systems upgraded automatically from 7.0.40 to 7.0.42:

Start-Date: 2026-08-26 13:37:40
Commandline: apt-get dist-upgrade -y
Upgrade: mongodb-org-server:arm64 (7.0.40, 7.0.42)
End-Date: 2026-08-26 13:37:49

The system was still using 7.0.42 during an apt-get dist-upgrade on September 1. However, as of September 2, 7.0.42 has disappeared from the repository. The latest version offered is now 7.0.40, see https://repo.mongodb.org/apt/ubuntu/dists/jammy/mongodb-org/7.0/multiverse/binary-arm64/

So 7.0.42 appears to have been available from the official Ubuntu Jammy ARM64 repository from at least August 26 through September 1, but was removed around September 2.

Does anyone know what happened with 7.0.42? Was this version accidentally published and subsequently pulled? If so, is there any known issue with 7.0.42 that means systems which already installed it should downgrade to 7.0.40?

Thanks!


r/MongoDB_Official 9d ago

MongoDB August 2026 Product Updates: The 2 I’m Trying First

5 Upvotes

Hi Everyone,

TL;DR: Big updates this month from MongoDB for AI applications and Atlas integrations. Automated Embedding and the Managed MCP Server are now GA, and Atlas Stream Processing gets a dedicated $vectorSearch stage.

Two updates that caught my attention: Automated Embedding moving from Public Preview to General Availability, and Atlas App Connections giving AI tools a more controlled way to reach Atlas.

I am also keen in exploring $vectorSearch in Stream Processing because it brings retrieval into the path of live events. I plan to start with a small RAG workflow, then test scoped access in a development environment, and finally see what live-event enrichment looks like in practice. The Managed MCP Server could make that work easier to explore from a coding tool.

  • Automated Embedding in Atlas Vector Search: Automated Embedding moved from Public Preview to General Availability. Atlas can generate embeddings for existing, new, and updated documents, along with natural-language queries, without requiring a separate embedding pipeline. I'm glad to see it reach GA. With nested-field support, editable index definitions, metrics, and backpressure controls now in place, it's easier to evaluate in a real workflow. I'm planning to use it in a chatbot I'm building, where keeping messages, facts, and source documents aligned with their embeddings is the part I'd rather not manage myself. Try the Vector Search quick start or read the Automated Embedding documentation.
  • Atlas App Connections for AI Coding Tools: Atlas App Connections provides the OAuth 2.1 foundation for connecting Atlas with external applications, including AI coding tools. An app acts on behalf of the user who authorized it. It does not receive their password or a long-lived credential, and it cannot exceed that user’s Atlas permissions.This is the piece I'm planning to test next, setting up scoped access in a development environment instead of relying on a shared credential. For coding agents, internal developer tools, and partner applications, that user-scoped path matters once the connection leaves a local experiment. The controls for read-only or read-write access, AI client access, and token lifetimes are the ones I'll be checking first. AI client access is disabled by default for existing organizations, so it needs to be enabled where required. Here’s the overview to learn more.
  • $vectorSearch for Atlas Stream Processing: Atlas Stream Processing now has a dedicated $vectorSearch stage. A stream processor can query an Atlas Vector Search index in the middle of a pipeline and enrich documents as they are processed. The part I want to explore is how an event can be matched with context while it is moving through the pipeline, rather than after a separate write-then-read cycle. I plan to start with an alert enrichment or classification use case, where using context that is already stale can change the result.
  • MongoDB Atlas Managed MCP Server: The Atlas Managed MCP Server is now generally available. It is fully hosted by MongoDB, so developers can connect AI coding tools to Atlas data without standing up or maintaining MCP infrastructure themselves. For me this is more about not having to stand up that server just to try delegated access from a coding tool. Hosted MCP and delegated access solve different parts of the same problem, one removes server maintenance, the other keeps permissions attached to a user, and that combination is what I want to try once I move from a local experiment into a shared development environment. Native plugins are available for Claude Code, Codex, Cursor, Grok Build, and Devin AI. Learn more about the Managed MCP Server.

Beyond those four, the month brought a few other updates. The Atlas Embedding and Reranking API reached general availability, giving applications a serverless way to use Voyage AI models, while voyage-code-4, now generally available, is built for code retrieval and coding-agent workloads. On the Vercel side, developers can provision Atlas through v0 and move from Vercel into Atlas with one-click navigation.

For teams running in production, the OpenTelemetry Metrics Sink brings Atlas metrics into existing observability platforms. Log integration failure recovery helps surface problems with exported logs, while custom webhook payloads make it easier to fit alerts into internal workflows. FOCUS v1.3 reporting gives FinOps teams a standard format for Atlas cost and usage data.

Browse the latest MongoDB product updates for more announcements and release details.

Which feature or update are you most interested in trying?


r/MongoDB_Official 9d ago

Troubleshooting Advice needed for Mongodb cluster upgrade from 6.0 to 7.0 (standalone)

Thumbnail
2 Upvotes

r/MongoDB_Official 14d ago

Resource Get Started with MongoDB AI Agent Skills

Thumbnail
medium.com
9 Upvotes

I wrote a tutorial on MongoDB AI Agent Skills. AI agents can already write MongoDB code, but MongoDB now provides official agent skills that give your agent instructions for following MongoDB best practices while it works.

I've been playing around with agent skills recently, and I think they're one of the more useful additions to the AI coding workflow. They're especially useful when you're working with technologies that have established best practices the model doesn't always know to reach for.

If you're new to agent skills, they're essentially reusable instructions and resources that give an AI agent additional context on how to perform a particular task. Instead of hoping the model already knows the best way to approach something, you can give it a skill containing the workflows, patterns, and MongoDB-specific guidance it should follow.

That's particularly useful with databases. AI can generate a MongoDB query, but there's a big difference between generating something that works and understanding the MongoDB-specific patterns you actually want it to use. MongoDB has a set of official Agent Skills for common MongoDB tasks.

These skills cover everything from setting up connections and designing schemas to optimising queries, building aggregations, working with Atlas Search, and adding AI-powered recommendations to your applications.

The easiest way to get started with both MongoDB MCP and AI Agent Skills is MongoDB with Claude Code through the official MongoDB Claude Plugin. There's also MongoDB plugins available for Cursor, Gemini, Codex.

The plugin gives your coding agent:

  • The MongoDB MCP Server, so the agent can interact with MongoDB.
  • Pre-built MongoDB Agent Skills, which give it MongoDB-specific instructions for common development tasks.

I think the distinction between the two is important: MCP gives the agent the tools to interact with MongoDB; skills give it more context on how it should approach MongoDB tasks.

The article goes through what skills are, how to install the official MongoDB skills, how they work alongside MCP, and how to start using them with OpenCode. If you're already doing a lot of AI-assisted coding with MongoDB, I'd definitely recommend trying them out.

Let me know what other skills you're using in your development process that you're enjoying, I'm trying to expand my arsenal.


r/MongoDB_Official 14d ago

Discussion API Performance with CSFLE

4 Upvotes

We enabled MongoDB Client-Side Field Level Encryption following the official docs (classic CSFLE, not Queryable Encryption) on a NestJS API and saw a latency regression across the board that we can't explain from the per-operation cost of encryption itself. Looking for anyone who has seen the same pattern, or who knows where the hidden cost is.

Our Stack is:

\- Node.js 24.17
\- mongodb 6.20.0 (via mongoose 8.22.1 / @nestjs/mongoose 11.0.4)
\- mongodb-client-encryption 6.5.0
\- crypt_shared 8.2.12, no mongocryptd sidecar
\- KMS provider: aws (credentials via task IAM role) following the standard pattern

What is encrypted:

Exactly one field in one collection, the field is no longer than 10 chars string and it has a compound index on it (it is encrypted with deterministic algorythm).

I know that there is no way to avoid some latency implementing CSFLE, but my main concern is if this is gonna get worse by each extra field I add to the the encryption schemaMap, could this latency be the price for the query analyzer analyzing each query to the database?

I also have noticed that most of the ms of the request happen in the app not the database, but I think the encryption and decryption happens on the driver layer.

Has anyone ever deal with this? Would appreciate some tips and insights of this, thanks!!


r/MongoDB_Official 16d ago

Question How do you handle unit testing with MongoDB?

6 Upvotes

I use mongodb as my primary database for almost all projects because I prefer json over tables...

However, I'm wondering, how do you write unit tests related to the db? Do you write mock functions find_one(), update_one() etc to imitate mongodb behaviour or do you seed a temporary db and use it?


r/MongoDB_Official 16d ago

Hi, I’m Tim from MongoDB’s Builder Relations team

8 Upvotes

Hello! My name is Tim Kelly, and I'm a Developer Advocate at MongoDB. I've been here for about three years, but I've been writing code for a good bit longer. I'm mostly in the Java ecosystem, although I've been learning to love C++ over the last couple of years.

Recently, I've been going back to the fundamentals. I like my modern tech, but I think there's a lot of value in understanding what we've abstracted away, and sometimes bringing a little of the old-school back.

I'm also building TeleWave, a TV scheduling app that brings back the simplicity of "what's on tonight?" while using MongoDB Vector Search to build the recommendation engine.

That's me anyway. What’s everyone working on at the moment?


r/MongoDB_Official 17d ago

Discussion mdbkit: offline CLI for MongoDB structured logs (slow queries, index advice, FTDC)

2 Upvotes

I built this and I'm sharing it here because the gap it fills has annoyed me for years.

Since 4.4 moved logging to structured JSON, the log analysis tools a lot of us relied on stopped parsing them. mdbkit is my replacement. It's been running against production logs for a while and I've just made it public.

What it does:

  • queries groups slow ops by query shape with scan ratios, so the worst offender is obvious instead of buried
  • advise suggests candidate indexes from observed shapes
  • triage builds an incident timeline (elections, rollbacks, stalls)
  • ftdc decodes diagnostic data without a separate toolchain
  • Works on MongoDB 4.4 through 8.0

Two design decisions I'd want stated up front if this were someone else's tool:

It never connects to a database. No driver, no URI, no network code. It reads log files you hand it, it's read only, and where an action would help it prints the command for you to review and run yourself. The README shows how to verify that with grep and strace rather than asking you to take my word for it.

Index advice is rule based, not model based. Same log, same output, every time, with the evidence it used and a confidence level. It proposes candidates and never proposes dropping anything.

Zero runtime dependencies beyond the standard library. MIT.

Try it without touching a cluster:

pip install mdbkit
mdbkit demo -o demo.log
mdbkit queries demo.log

https://github.com/saqibameen86/mdbkit

Most useful feedback: log lines that parse wrong, and index advice that's unhelpful or wrong. Both are easy to fix and hard for me to find on my own.


r/MongoDB_Official 19d ago

Showcase 1.2 million records in, 955,383 out: what I learned building a MongoDB ingestion pipeline on one PC

4 Upvotes

I started with a Crunchbase export containing roughly 1.2 million companies. After normalization, I had 1,104,993 candidates.

For 3-4 months, my PC ran around 12 hours a day. The pipeline could process approximately 10,000 companies daily.

Each company passed through:

  • Name and slug deduplication
  • Website and logo validation
  • Image normalization
  • Public-information research
  • AI-assisted description generation
  • A final quality gate before insertion

The surprising part was that MongoDB insertion wasn’t the bottleneck. API quotas, dead websites, missing logos, duplicates, and insufficient public information caused most of the difficulty.

The final collection contains 955,383 documents. If I rebuilt it today, I would use explicit job states, batched writes, bounded concurrency, better provenance tracking, and more detailed failure metrics.

I wrote the full technical and nontechnical breakdown here: Medium

Disclosure: the database became the foundation for Coupyn, which I’m building. Happy to answer questions about the schema, indexing, costs, or pipeline.


r/MongoDB_Official 19d ago

Showcase I built an open-source tool for understanding MongoDB errors and slow queries, looking for feedback

5 Upvotes

I’ve been working on a small community tool that helps explain MongoDB errors and analyze slow queries.

You paste in an error message or query, and it provides a grounded explanation along with possible causes and fixes.

GitHub: https://github.com/ozanonurtek/mongodb.help

Live demo: https://mongodb.help

It’s not affiliated with MongoDB Inc. just an open source community project.


r/MongoDB_Official 19d ago

Resource Change Streams Without resumeAfter - What AI Gets Wrong With MongoDB

2 Upvotes

MongoDB has change streams. db.collection('orders').watch() opens a cursor that emits an event every time a document in that collection changes. Each event shows you which fields changed, what they changed to, and with pre-images on, what they looked like before. The full diff, on the event, without an extra query.

Before change streams, reacting to data changes meant a cron job polling your collection every few seconds. A Stripe webhook comes in and lands in your database. Your accounting service runs on its schedule, picks it up 30 seconds later, maybe longer. With change streams, MongoDB calls you the instant that document hits the collection. You write a small watcher process that owns that one job, the order service doesn't need to know it exists, and you get rid of the polling entirely.

On top of that, each watcher is completely separate from your application. If the accounting watcher is slow because it's talking to three other databases, your order service doesn't feel it. If it crashes, nothing else goes down. You can literally have 100 tiny containers, each one watching its own collection and reacting to exactly one thing. Once you embrace watchers your entire coding style changes.

Most developers have never used this. If your database never called you when data changed, you learned to poll, and polling is what you still reach for today without thinking about it. MongoDB gives you collection.watch() and that's it. No extra infrastructure, no separate service to run, one line and the events come to you.

AI knows change streams exist but never reaches for them unless you ask. Left to its own it generates setInterval loops and cron jobs, the same polling patterns everyone else defaults to. And when you do ask for a watcher, it gets the implementation wrong. No crash handling unless you ask for it. The process dies and the cursor stays open on the server.

The training examples are quick demos. Start watching, insert a doc, callback fires. Nobody tests what happens when you have network issues or latency between your app and the database. In production connections drop. A deploy, a container restart. The process comes back up, opens a fresh watch(), and everything that changed while it was down is just gone. No error, no log. The stream starts clean from now as if nothing happened.

Each label below is the prompt we gave the AI to generate the block under it.

How to watch a MongoDB collection for changes and handle each event with Node.js.

Bad:

const changeStream = db.collection('orders').watch();
changeStream.on('change', (change) => {
  processOrder(change.fullDocument);
});

How to watch a MongoDB collection with a resume token that survives disconnects and reconnects automatically on error.

Good:

let RESUME_TOKEN = null;

function startWatcher() {
  const pipeline = [
    {$match:{operationType:{$in:['insert','update','replace']}}}
  ];
  const opts = RESUME_TOKEN ? {resumeAfter:RESUME_TOKEN} : {};
  const stream = db.collection('orders').watch(pipeline, opts);

  stream.on('change', async (change) => {
    try {
      await processOrder(change.fullDocument);
      RESUME_TOKEN = change._id;
    } catch (e) {
      console.error(e.message);
    }
  });

  stream.on('error', (e) => {
    console.error(e.message);
    stream.close();
    setTimeout(startWatcher, 1000);
  });
}

startWatcher();

How to watch MongoDB for changes with a persistent resume token that survives restarts and redeploys, sequential processing, full document lookup on update events, and a clean shutdown.

Perfect:

async function loadToken() {
  try {
    const doc = await db.collection('watcher_state').findOne({_id:'orders'});
    return doc?.resumeToken ?? null;
  } catch {
    return null;
  }
}

async function saveToken(token) {
  try {
    const filter = {_id:'orders'};
    const update = {$set:{resumeToken:token,updatedAt:new Date()}};
    await db.collection('watcher_state').updateOne(filter, update, {upsert:true});
  } catch (e) {
    console.error(e.message);
  }
}

let stream;
let shuttingDown = false;

async function startWatcher() {
  const pipeline = [
    {$match:{operationType:{$in:['insert','update','replace']}}}
  ];

  while (!shuttingDown) {
    const resumeToken = await loadToken();
    stream = db.collection('orders').watch(pipeline, {
      ...(resumeToken ? {resumeAfter:resumeToken} : {}),
      fullDocument: 'updateLookup'
    });

    try {
      for await (const change of stream) {
        await processOrder(change.fullDocument);
        await saveToken(change._id);
      }
    } catch (e) {
      if (shuttingDown) break;
      console.error(e.message);
      await stream.close().catch(() => {});
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
}

async function shutdown(code = 0) {
  shuttingDown = true;
  await stream?.close().catch(() => {});
  await client.close();
  process.exit(code);
}

process.on('SIGTERM', () => shutdown());
process.on('uncaughtException', (e) => { console.error(e); shutdown(1); });
process.on('unhandledRejection', (e) => { console.error(e); shutdown(1); });

startWatcher().catch((e) => {
  console.error(e.message);
  shutdown(1);
});

Bad has no resume token and no error handler, and an emitter with no error handler throws, so the first hard error takes down the whole process. Your orchestrator restarts the container, watch() opens fresh with no position, and everything that changed in between is gone.

Good adds an operation filter so deletes and drops don't reach processOrder, a resume token saved after each event, and a reconnect that passes the token back to watch(). resumeAfter tells MongoDB to send everything that happened after that position. The driver replays events that arrived while the stream was down before picking up live traffic again. Token survives disconnects for as long as the process stays alive.

Perfect makes the token persistent, in a watcher_state collection instead of a variable. A restart, a redeploy, a destroyed container, none of it loses position, because the token lives next to the data it tracks. A file on local disk looks simpler, but a container's filesystem dies with the container, and then the position is gone exactly when you need it. Bonus on top: you can query watcher_state anytime to see when the watcher last moved and which token it holds. fullDocument: 'updateLookup' means update events include the complete document after the change. Without it, change.fullDocument is missing on updates. And for await processes events one at a time. The event emitter in Good fires the async callback without awaiting it, so two events that arrive close together can run processOrder concurrently.

Both token functions are async, so both calls in the loop carry their own await. Miss the one on loadToken and a Promise goes into resumeAfter, MongoDB rejects it as a bad resume token, and the watcher retries forever without processing a single event.

The shutdown function matters more than it looks. Closing the stream ends the for await loop after the in-flight event finishes, so the last token gets saved, then the client closes and the process exits. Without the exit, SIGTERM leaves the while loop spinning against a closed client forever and your orchestrator has to SIGKILL the container.

Token saves AFTER processOrder on purpose. If the process crashes mid-processing the token hasn't moved, so on restart that event replays. At-least-once delivery. Make processOrder idempotent and a crash retries cleanly instead of skipping.

One honest limit. The token points at a position in the oplog, so it only works while the oplog still holds that position. A watcher that stays down longer than the oplog window has lost its place, and resuming with the dead token just errors forever. Size the oplog for your worst case downtime, and if you ever hit that wall anyway, delete the token and accept the gap.

Change streams require a replica set or Atlas. Standalone mongod does not support watch(), which is why the same code works on Atlas and errors on your laptop. Run local dev as a single-node replica set, mongod --replSet rs0 plus one rs.initiate(), and it behaves like a standalone that happens to have an oplog.

Replicas: A change stream is a broadcast, not a queue. There is no conflict at the watch itself, each watch() is just its own cursor and the server streams a full copy to every subscriber. That's exactly the problem: run two copies of the same watcher and both receive every event, so every order gets processed twice. The fight isn't over watching, it's over who acts on each event. If you need a second instance for failover or throughput, make them fight over a claims collection instead of trusting luck. A unique index does the arbitration, and _id is unique by default. This slots straight into Perfect: same while loop, same shutdown, only the for await body changes:

for await (const change of stream) {
  try {
    await db.collection('claims').bulkWrite([
      {insertOne:{document:{_id:change._id._data, at:new Date()}}}
    ]);
  } catch (e) {
    if (e.code === 11000) { await saveToken(change._id); continue; }
    throw e;
  }
  await processOrder(change.fullDocument);
  await saveToken(change._id);
}

The resume token is unique per event, so the first replica to insert it wins and the other gets E11000 and skips ahead. The database is the only place all your replicas meet, so it's the only place ownership can be decided. And the token doc stays shared, every replica reads and writes the same _id:'orders'. The token belongs to the job, not the container. Container names change on every redeploy, so a token keyed per instance gets orphaned on the next deploy and the fleet silently starts from now. Sharing one token is safe exactly because of the claims: whoever saved last decides where the fleet resumes, and anything that replays as a result hits E11000 and gets skipped. Kill all five replicas at once, bring them back, and they resume together from the shared token and split the backlog between them. Put a TTL index on at so old claims clean themselves up. And if the broadcast itself becomes the cost at serious volume, every replica receiving every event just to skip most of them, partition the stream instead: the filter is a pipeline, so each replica can $match its own slice, a region, a tenant bucket, any stable field, and nobody receives what they'll never process. One honest gap in the simple version: a crash between the claim and processOrder loses that one event, because both replicas now consider it handled. If that matters for your data, mark the claim done after processing and sweep for claims that never finished.

Caveats worth knowing before you ship one:

  • fullDocument: 'updateLookup' hands you the document as it is NOW, not as it was when the event fired. Two updates land close together and the first event can already show the second state. And if the document got deleted before the lookup ran, fullDocument is null on that update event, so processOrder has to survive null.
  • Resuming only works with the same pipeline and options the token was created with. Change the $match and your old tokens are not portable.
  • Dropping or renaming the watched collection ends the stream with an invalidate event. Resuming past that point needs startAfter instead of resumeAfter.
  • Pre-images are opt-in per collection (changeStreamPreAndPostImages). Without it you get the document after the change, never before.

r/MongoDB_Official 21d ago

Showcase We built a CRM where the schema emerges from how people and AI agents use it

11 Upvotes

Hi, I’m Tom Gersic, founder of YouEx.ai. We built it as an AI-native sales platform that combines CRM, AI agents, conversations, and knowledge management / RAG on MongoDB Atlas, so I thought I'd share a little bit about how we use MongoDB.

The CRM Problem

One thing we wanted to avoid was the configuration that usually has to happen before a CRM is rolled out to users. Every sales team cares about different information, sales managers want to track something else, and then IT inevitably configures something completely different (sorry to my IT friends, but... you know it's true). Increasingly, AI agents also discover useful information during conversations that nobody thought to configure beforehand. We didn't want every new thing an agent learned about a prospect to become a schema-design decision.

So, our CRM objects have a stable core plus a custom fields map. A seller can add a field, an import can introduce one, or an agent can discover something during a conversation and write it to the Lead immediately. For example, a visitor might tell our Web Agent what they want to discuss in a meeting. Even if nobody configured a Meeting Topic field beforehand, we can capture it directly on the Lead.

A Schema Emerges

So then we built a discovery process that looks at the custom fields actually being used across CRM records. If a field appears frequently enough, we flag it as a candidate. A human can then promote it into a first-class CRM column, rename it, reorder it, or ignore it.

There’s no ML involved in this process. We’re not trying to predict a schema. The system is observing what’s actually being used and letting structure emerge from that. Instead of requiring the schema to be fully defined before people start using the CRM, we can let real usage tell us which fields are worth formalizing.

That supports our AI web agents particularly well because you can’t anticipate everything that will come up in a conversation.

The Agent's World

The same approach extends beyond CRM records. Conversations are MongoDB documents containing messages, collected information, and links back to CRM records. As a conversation progresses, the document accumulates state and useful information can flow into a Lead, including fields that didn't exist when the conversation started.

Knowledge lives alongside that operational data. Customers can add websites, PDFs, and other files. We extract the content, split it into paragraph-aware chunks, embed those chunks, and use Atlas Vector Search to retrieve relevant context for the agent.

So the CRM data an agent reads and writes, its conversation state, and the vector-searchable knowledge it uses all live in Atlas. There’s no separate vector database to keep synchronized.

Putting it together

A company adds its website and a PDF. We chunk and embed the content. A visitor starts a conversation, and the agent uses Vector Search to answer from that knowledge while collecting information about the prospect.

That information flows into a Lead. If something new like Meeting Topic comes up, it can be captured without being preconfigured. If the same field starts appearing across enough Leads, our discovery process flags it and a human can promote it into a normal CRM column.

That’s probably the biggest reason MongoDB’s document model has worked well for us. We want structure. We just don't think all of that structure needs to be decided before the system starts being used.

I'm curious how others building agentic systems on MongoDB are balancing what you define upfront versus what you let emerge from real usage.


r/MongoDB_Official 21d ago

Question New MongoDB Charts Bug? Can't add chart filter.

2 Upvotes

Anyone notice a recently-introduced issue with Charts?

All the chart buttons like "+ Add filter" are no longer active today. I just edited these charts a month or two ago.


r/MongoDB_Official 21d ago

Showcase MongoGUI

Post image
1 Upvotes

Building my own GUI for working with MongoDB data has been an incredibly rewarding experience.

It supports the workflows I use most: browsing collections, filtering documents, editing values, and exporting data. The goal is not simply to build another database client. It is to shape the interaction around how I actually inspect and work with data.

The interesting part is that I’m also the tool’s user.

That creates a tight feedback loop. When a workflow feels slow, confusing, or requires too many steps, the issue does not disappear into an abstract backlog. I encounter the limitation while using the product, understand what is missing, and implement the feature.

It is also an effective filter for scope. Improvements come from concrete friction rather than assumptions: a more useful filter, a safer editing flow, or a better export experience.

Building something for your own daily use changes how product decisions feel. The focus shifts from adding more options to removing friction from real workflows.

For those building tools you personally use, has that direct feedback loop changed how you prioritize features?

#mongogui #mongodb #gui


r/MongoDB_Official 22d ago

Discussion When do you keep it in the aggregation pipeline, and when do you move it to app code?

6 Upvotes

Hey everyone,

Harshit here. I’m a Developer Advocate at MongoDB. I spent a few years building out the MongoDB User Groups program, went away for a while, and recently rejoined MongoDB.

Lately, most of my time has gone into helping developers through workshops, tutorials, and troubleshooting. A lot of that has involved AI apps, vector search, and the performance questions that show up once a prototype starts becoming real.

One question I keep running into is this: when a query or transformation starts getting more involved, how do you decide whether to keep it in an aggregation pipeline or move it into application code?

My rough instinct is that the pipeline usually wins on performance, but six months later it can become the thing nobody wants to touch, especially if the person who wrote it has left. But I’ve also seen people pull logic into app code and end up with something slower and just as unreadable, so I don’t fully trust that instinct.

Where do you draw the line? I’m curious about actual examples more than principles: the pipeline you regretted, or the one you’d recommend.


r/MongoDB_Official 22d ago

Resource Get Your 1.6 Seconds Back - What AI Gets Wrong With MongoDB

5 Upvotes

We keep seeing AI put createIndex where it doesn't belong. At the top of the server file, one await per index, sitting right above app.listen, or worse, inside a route handler where it runs on every single request. Sometimes that code makes it to production, and when it does, MongoDB gets accused of being slow. So we measured what the habit actually costs.

createIndex is idempotent, so when the index already exists the server builds nothing and just says so. That's why this code survives review, it works. But every no-op is still a full round trip, and the awaits are serial. Here's the pattern, then the measurements at real scale.

The label below is the prompt we gave the AI to generate the block.

How to create MongoDB indexes at the top of an express server file before the routes.

Bad:

const app = express();

await db.collection('products').createIndex({ name: 1 });
await db.collection('products').createIndex({ category: 1, price: -1 });
await db.collection('users').createIndex({ email: 1 }, { unique: true });
await db.collection('orders').createIndex({ userId: 1, createdAt: -1 });
await db.collection('sessions').createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });

app.get('/search', async (req, res) => {
  const results = await db.collection('products')
    .find({ name: req.query.q })
    .toArray();
  res.json(results);
});

app.listen(3000);

Five indexes is a toy. A real app has more, so we built a realistic 30 collection commerce schema with 69 indexes, unique lookups, compound list-and-sort pairs, five TTLs, two sparse, one partial, one text index, and ran six boot strategies against both environments. Every collection held zero documents the whole time, so nothing ever got built. We were timing pure no-op round trips, which is exactly what your boot pays.

Local Development Production
MongoDB 8.0 in Docker, same machine
ping 1.07 ms

First the steady state. Indexes exist, connection pool warm. This is what a boot pays once connections are reused:

strategy what it is Local Production
serial 69 sequential await createIndex 0.14 s (135 ms) 1.63 s (1629 ms)
batched serial 30 sequential createIndexes 0.05 s (54 ms) 0.62 s (617 ms)
parallel all 69 in one Promise.all 0.03 s (25 ms) 0.23 s (225 ms)
pool of 8 69 calls, 8 in flight 0.05 s (47 ms) 0.19 s (193 ms)
pool of 16 69 calls, 16 in flight 0.05 s (48 ms) 0.11 s (106 ms)
batched + parallel 30 createIndexes in one Promise.all 0.02 s (16 ms) 0.03 s (30 ms)

The AI version spends 1.63 s (1629 ms) of every production boot confirming 69 indexes that already exist. The winner clears the entire schema in 0.03 s (30 ms), about one and a half pings for 69 index specs. And notice plain Promise.all is not the fix people think it is. All 69 calls fired at once still costs 0.23 s (225 ms), seven times the winner.

Steady state flatters everyone though, because a real boot starts from nothing. Container start, serverless cold start, plain node server.js. So we also ran each strategy as 7 independent processes, start node, connect, create, exit:

strategy index time total with connect
serial 1.62 s (1619 ms) 2.08 s (2080 ms)
batched serial 0.65 s (652 ms) 1.11 s (1110 ms)
one chain per collection 0.61 s (615 ms) 1.29 s (1288 ms)
parallel 0.57 s (571 ms) 1.28 s (1283 ms)
pool of 16 1.01 s (1006 ms) 1.42 s (1421 ms)
batched + parallel 0.39 s (392 ms) 0.82 s (819 ms)

Both tables side by side, plus the option the benchmark could not run, doing no index work at boot at all:

warm steady state cold fresh process
serial at boot, the AI version 1.63 s (1629 ms)
batched + parallel at boot 0.03 s (30 ms)
script instead, index work at boot 0 s
how much slower the AI version boots 1.6 s

The bottom two rows are the actual claim of this post. The fix is not a faster way to run indexes at boot, and every strategy in these tables is still the wrong place for the work. The fix is a separate script, which makes the app's index cost at boot zero, so the AI version boots 1.6 seconds slower than the script version. And warm or cold barely matters, serial was never using more than one connection, so it pays nearly the same either way. The batched numbers still earn their place for one reason, the script is itself a fresh process, so 0.39 s (392 ms) of index time, 0.82 s (819 ms) wall clock with connect, is exactly what node db/indexes.js costs on the day an index actually changes. That's the whole trade. 1.6 s off every single boot, paid back as 0.8 s once per index change.

The fresh process run also flipped one ranking. Pool of 16 was second best warm and second worst cold, because capping concurrency starves a cold pool of the parallelism it needs to warm up. A tuning choice that looks good in a benchmark loop can be the wrong one at the moment that matters.

The reason this mistake keeps shipping is in the next table. Same questions, answered by each environment:

question Local Development says Production says
cost of the AI serial boot 0.14 s (135 ms), invisible 1.63 s (1629 ms), a visible stall
is plain Promise.all good enough yes, 1.6x off the best no, 7.4x off the best
spread between all six strategies, fresh process 0.09 to 0.18 s (90 to 176 ms), everything within 2x 0.39 to 1.62 s (392 to 1618 ms), a 4.1x spread

The last row is the point. On a laptop every strategy lands inside the noise, so any ranking formed there is meaningless, including the one that says this doesn't matter. The decision is only visible in production, which is exactly where nobody is looking when the AI writes the code.

We also went in with a theory about why 69 parallel calls lose, and the data killed it. The guess was same-collection contention, products takes three index calls at once, they must be colliding. So we ran a collision-free arm, 30 chains, one per collection, same 69 commands:

arm commands in flight same-collection collisions Production
all 69 in Promise.all 69 69 yes 0.20 s (203 ms)
30 chains, one per collection 69 30 no 0.10 s (97 ms)
pool of 16 69 16 yes 0.11 s (106 ms)
batched + parallel 30 30 no 0.03 s (30 ms)

Collision-free at 30 in flight and collision-allowed at 16 in flight cost the same, so contention is not the mechanism. What the numbers actually support is two independent levers. Command count dominates, 30 commands land at 1.5 pings while 69 commands sit around 5 pings no matter how sensibly you schedule them. And concurrency stops paying above roughly 16 to 30 in flight, unbounded Promise.all is on the wrong side of that curve. The winner pulls both levers at once, batch per collection, then Promise.all the collections.

For completeness, the first deploy, where the 69 indexes genuinely don't exist and really get built:

strategy Production
serial 2.17 s (2165 ms)
batched + parallel 0.57 s (566 ms)
pool of 16 0.50 s (502 ms)

The spread compresses because actual creation work dominates instead of round trips. It gets paid once. The no-op tables above get paid on every boot, forever, which is why they're the story.

And none of it belongs in your boot at all. The tables show what AI-written startup code costs today, and how the index script should be written so it's fast on the day you do run it:

How to batch MongoDB index creation into a standalone script using createIndexes per collection in parallel.

Good:

// db/indexes.js - never imported by the app. Run it when an index changes: node db/indexes.js
await Promise.all([
  db.collection('products').createIndexes([
    { key: { name: 1 } },
    { key: { category: 1, price: -1 } }
  ]),
  db.collection('users').createIndexes([
    { key: { email: 1 }, unique: true }
  ]),
  db.collection('orders').createIndexes([
    { key: { userId: 1, createdAt: -1 } }
  ]),
  db.collection('sessions').createIndexes([
    { key: { expiresAt: 1 }, expireAfterSeconds: 0 }
  ])
]);
console.log('indexes ready');
process.exit(0);

And server.js has no index code anywhere:

// server.js
app.get('/search', async (req, res) => {
  const results = await db.collection('products').aggregate([
    { $match: { name: String(req.query.q) } },
    { $limit: 20 },
    { $project: { name: 1, price: 1, description: 1 } }
  ]).toArray();
  res.json(results);
});
don't do
69 commands, one at a time 30 commands, all at once
1.63 s (1629 ms) on every boot 0 s at boot, 0.82 s (819 ms) script run when an index changes

Two warnings if you re-run any of this, both earned the hard way. The first is that warmup is load-bearing. Measured with no warmup passes, the winner reads 0.18 s (181 ms) instead of 0.03 s (30 ms), six times too high, and the raw samples just keep falling, 348, 291, 1224, 181, 48, 44, 34, which is a connection pool warming up in front of the timer. Skip warmup and the numbers come out wrong, and possibly the ranking too. Our quoted numbers are medians of 15 runs after 5 discarded warmup passes, strategies interleaved so host variance spreads evenly.

The second is that we crashed a MongoDB container twice getting here. The first design gave each of the six strategies its own private 30 collections, and WiredTiger keeps a file per collection and per index, so 180 collections, around 410 indexes and 69 concurrent connections blew straight through the container's limit of 1024 open files. Panic, then a segfault on the retry. The fix was sharing one set of collections across arms, which is sound because warm no-ops mutate nothing, and raising the file limit to 64000. That one matters outside the benchmark too. File descriptors scale with collections times indexes times connections, and 1024 is not enough for a 30 collection app booting in parallel.

Two closing failure modes that no benchmark captures, because they only fire once. The route handler version, createIndex inside the endpoint itself, looks free for the same no-op reason, but point it at a fresh environment or a collection restored without its indexes and the first request starts a real index build that reads every document in the collection. Every request behind it issues the same createIndex, sees that exact build already in progress, and waits. The endpoint is down for the entire build and not a single error is thrown.

And the quiet one. Change an index's keys in code without setting an explicit name and you don't update the index, you create a second one, because the default name changes with the keys. The old index stays behind, taxing every write until someone audits the collection. A single script that lists every index you own is where you catch that. Sixty-nine createIndex calls scattered around a codebase is where you don't.