A MongoDB replica set can run comfortably for months and then begin showing memory pressure without any obvious infrastructure change. Queries that previously completed quickly start taking longer, disk reads increase, and application response times become inconsistent.
The first reaction is often to increase server RAM. That may be necessary, but it does not explain why the existing deployment has become slower.
Memory pressure can develop for several reasons. The dataset may have grown, new indexes may consume additional memory, query patterns may have changed, or more concurrent operations may be competing for the same resources. In a replica set, the problem can also affect members differently because they may handle different workloads.
The important part of the investigation is understanding what MongoDB actually keeps in memory, which replica set member is under pressure, and whether the problem comes from data growth or inefficient access patterns.
The Working Set Does Not Have to Fit Entirely in RAM
The working set is the portion of a database that applications access frequently. It includes the data and indexes needed by normal query and update operations.
For example, consider an ecommerce application with 50 million product records. Customers frequently search for active products, check prices, and retrieve product details. Although the collection contains millions of documents, the application may repeatedly access only a smaller portion of those records.
That frequently accessed portion forms much of the application's working set.
If the working set remains available through memory and the filesystem cache, MongoDB can avoid unnecessary disk reads. If frequently accessed pages are repeatedly evicted and loaded again, query performance can deteriorate.
This does not mean that the entire database must fit in RAM. MongoDB's WiredTiger storage engine uses two important memory areas:
WiredTiger internal cache: Stores data and index pages in the storage engine's internal representation.
Operating system filesystem cache: Keeps file data in memory to reduce physical disk reads.
These areas work together. Increasing the WiredTiger cache without considering filesystem cache and other memory consumers can make the overall situation worse.
Understand What Is Consuming MongoDB Memory
Before investigating individual queries, understand how MongoDB allocates memory.
WiredTiger uses an internal cache for data and indexes. By default, its cache size is the larger of 50% of the available RAM minus 1 GB, or 256 MB.
For example, on a server with 16 GB of RAM, the default calculation is:
WiredTiger cache = 50% × (16 GB - 1 GB)
WiredTiger cache = 7.5 GB
The remaining memory is not simply unused. The operating system can use available RAM for filesystem caching, while MongoDB and other processes also need memory for their operations.
This is why the WiredTiger cache size should not be treated as the total memory requirement of a MongoDB server.
Other memory consumers include:
MongoDB connections and active operations.
Aggregation pipelines and sorting operations.
In-memory execution structures.
Replication and background processing.
Operating system processes.
Monitoring agents and other applications running on the host.
If multiple MongoDB instances share one machine, each instance also competes for the same physical memory.
A deployment that worked with one mongod process may experience pressure after another database process or monitoring service is introduced.
Check WiredTiger Cache Usage
The first useful diagnostic is MongoDB's serverStatus command.
Run it against the affected replica set member:
db.serverStatus().wiredTiger.cache
The result contains several fields that help explain cache activity.
For example:
{
"maximum bytes configured": 8053063680,
"bytes currently in the cache": 7500000000,
"tracked dirty bytes in the cache": 350000000,
"pages read into cache": 1250000,
"pages written from cache": 850000,
"pages evicted by application threads": 1200
}
These values are illustrative. Actual field names and available metrics can vary by MongoDB version.
Several metrics deserve attention:
maximum bytes configured: The configured WiredTiger cache limit.
bytes currently in the cache: The amount of data currently held in the internal cache.
tracked dirty bytes in the cache: The amount of modified data that has not yet been written out.
pages read into cache: Pages loaded into the cache from storage.
pages written from cache: Pages written out from the cache.
pages evicted by application threads: Eviction work performed by application threads.
A cache that regularly approaches its configured limit deserves investigation, but a high cache utilization percentage alone does not prove that the database has a problem. WiredTiger is designed to use its available cache.
Look at the metrics over time. If cache usage remains high while page reads, eviction activity, and query latency also increase, there may be genuine memory pressure.
You can also retrieve the complete server status:
db.serverStatus()
For ongoing monitoring, collect the relevant metrics at regular intervals instead of relying on one snapshot.
Check Whether the Problem Affects Every Replica Set Member
A replica set does not necessarily have identical memory usage on every member.
The primary usually handles application writes and may also serve reads. Secondary members replicate changes from the primary, but they may receive different read traffic depending on the application's read preference.
For example, an application may direct reporting queries to a secondary while normal transactional requests go to the primary. The reporting workload could create a much larger working set on that secondary.
Start by checking the replica set status:
rs.status()
Identify the primary and secondary members, then collect serverStatus metrics from each member separately.
Compare:
Suppose the primary has stable cache usage while one secondary shows increasing page reads and disk activity. That points toward a workload-specific problem rather than a replica set-wide shortage of RAM.
Do not automatically increase memory on every member before understanding the difference. The primary and secondary nodes may need different investigation and capacity planning.
Find Out Whether Index Growth Is Increasing Memory Pressure
Indexes are often overlooked when a database starts consuming more memory.
A new index can improve one query while increasing the memory required by the database. Larger indexes also increase storage usage and can create additional write work.
Consider a collection that initially has indexes on customer ID and order status. The development team later adds indexes for reporting, filtering, sorting, and several combinations of fields.
Over time, the index footprint can become significantly larger.
Inspect collection statistics:
db.orders.stats()
Useful fields include:
count: Number of documents.
size: Logical collection data size.
storageSize: Allocated storage size.
totalIndexSize: Total size of collection indexes.
indexSizes: Size of individual indexes.
To list indexes:
db.orders.getIndexes()
Review whether every index still supports an active query pattern.
An index that was useful for an old reporting endpoint may no longer be needed. Another index may duplicate the leading fields of an existing compound index.
However, do not remove an index simply because it is large. First inspect query plans, application usage, and the impact of removing it.
An index that appears unused during a short observation period may still support a monthly report or an infrequent administrative operation.
Investigate Queries That Read Too Much Data
A growing working set is not always caused by an increase in the number of documents. Sometimes the application has started accessing more data than it needs.
For example, an endpoint that previously returned a small number of customer records may now scan thousands of documents because a filter or index is no longer effective.
Use explain to inspect the execution plan:
db.orders.find({
customerId: 10025,
status: "Pending"
}).explain("executionStats")
Pay attention to:
nReturned: Number of documents returned.
totalKeysExamined: Number of index keys examined.
totalDocsExamined: Number of documents examined.
executionTimeMillis: Execution time for the operation.
If totalDocsExamined is much larger than nReturned, the query may be examining more documents than necessary.
For example, returning 20 records after examining 200,000 documents is a sign that the query deserves attention.
This does not automatically mean that an index is missing. The filter, sort order, index definition, and data distribution all affect the execution plan.
An aggregation pipeline can create similar pressure. Large intermediate result sets, blocking sorts, and grouping operations may consume substantial memory even when the final response contains only a few records.
Review expensive operations before assuming that the WiredTiger cache itself is incorrectly configured.
Watch Eviction and Disk Activity
When WiredTiger needs additional cache space, it evicts pages that are eligible for removal.
Eviction is normal. MongoDB does not need to keep every page in memory permanently.
The problem appears when the database repeatedly removes pages that the application needs again shortly afterward.
This can create a cycle:
A query loads data into memory.
Other operations consume cache space.
WiredTiger evicts previously loaded pages.
Another query needs those pages again.
MongoDB reads them from storage.
If this happens frequently, disk activity increases and query latency can become inconsistent.
Check the WiredTiger eviction metrics in serverStatus and compare them with operating system disk statistics.
The WiredTiger cache also has internal thresholds that trigger eviction activity. When cache pressure becomes severe, application threads may participate in eviction work. This can increase latency because threads that would otherwise process application requests are helping free cache space.
Long-running transactions can make this situation worse. Pages associated with active transactions may not be immediately eligible for eviction, preventing the storage engine from freeing enough space.
For this reason, inspect long-running transactions when cache pressure appears alongside unexpected eviction activity.
Check Container and Host Memory Limits
One common source of confusion is the difference between physical server RAM and the memory actually available to mongod.
A MongoDB process running in Docker or Kubernetes may have a container memory limit smaller than the host's available RAM.
For example, a server may have 32 GB of physical memory, while the MongoDB container is limited to 12 GB.
If the WiredTiger cache is configured based on the host's memory rather than the container's available memory, other processes inside the container may run out of memory.
Check the memory limit MongoDB detects:
db.hostInfo()
Review the returned system information and compare it with the container's configured memory limit.
In Kubernetes, also inspect the resource configuration:
resources:
requests:
memory: "8Gi"
limits:
memory: "12Gi"
The example shows a 12 GiB memory limit. It does not mean that the WiredTiger cache should also be configured to 12 GiB.
The cache must leave enough room for MongoDB's other memory requirements and the environment's overhead.
If the host runs multiple mongod instances, the cache configuration should account for all of them rather than treating each process as the only database server on the machine.
Should You Increase the WiredTiger Cache Size?
Increasing the cache size can help when the working set is larger than the available internal cache and the system has sufficient memory.
However, increasing it without measuring the other memory consumers can reduce filesystem cache and leave less memory for the operating system.
MongoDB recommends avoiding an increase above the default WiredTiger cache size unless the workload requires it.
If a change is justified, the cache can be configured using storage.wiredTiger.engineConfig.cacheSizeGB or storage.wiredTiger.engineConfig.cacheSizePct.
For example:
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 6
This example configures a 6 GB internal cache. It is not a recommended value for every server.
A production change should be based on observed memory usage, container limits, other processes, and the workload's cache behavior.
Also remember that cache configuration is applied to each mongod instance. In a replica set, changing the setting on one member does not automatically configure the others.
Test changes carefully and monitor the effect on query latency, disk activity, memory availability, and replication behavior.
A Practical Investigation Sequence
When a replica set starts showing memory pressure, investigate in a consistent order.
First, compare memory usage across all members. Determine whether the issue is isolated to one node or affects the entire replica set.
Next, inspect WiredTiger cache metrics and operating system memory. Look for sustained cache pressure, increased page reads, eviction activity, and swapping.
Then examine collection and index growth. Compare current statistics with earlier monitoring data if available.
After that, inspect slow queries and execution plans. Identify operations that scan too many documents, return unnecessary fields, or create large intermediate result sets.
Finally, check long-running transactions, container memory limits, and recent workload changes.
This sequence helps separate a capacity problem from a query or configuration problem.
Conclusion
When a MongoDB replica set no longer fits its previous memory profile, adding RAM is only one possible solution. The underlying cause may be a larger working set, unnecessary indexes, inefficient queries, different workloads across replica set members, or an incorrect container memory configuration.
Start with serverStatus and WiredTiger cache metrics, then compare the results across the primary and secondary members. Review index sizes, query execution statistics, eviction activity, and operating system memory before changing cache settings.
The goal is not to force the entire database into RAM. It is to keep frequently accessed data available efficiently while leaving enough memory for MongoDB, the operating system, and other processes.
Once the source of memory pressure is clear, the team can decide whether to optimize queries, remove unnecessary indexes, adjust cache configuration, or increase server capacity.
Join the conversation! Your thoughts help the community grow.