Intro
MongoDB is not just a database you install and forget. In production, it is a complex distributed system with its own internals, architecture, and failure modes. If you treat it as a black box, you will eventually be surprised — usually at 3 a.m. during an incident.
This guide is for developers, DevOps engineers, and startup teams who need to operate MongoDB safely. It covers advanced concepts such as version inventory, configuration changes, diagnostics, failure recovery, and daily operations. Each section includes concrete commands, expected outputs, and recovery paths. The goal is operational safety: observe before changing, limit blast radius, use placeholders rather than secrets, verify results, and document how to roll back.
We will use mongosh, the MongoDB Shell, for all examples. Commands are version-specific; always check your installed version first.
Version and Environment Inventory
Before touching any MongoDB deployment, you must know exactly what you are running and how it is configured. This is the foundation of safe operations. A version inventory prevents mismatched drivers, unsupported features, and incompatible upgrades.
Identify the Installed Version
Run this read-only command in mongosh:
db.version()
Expected output (example):
'6.0.5'
Record the exact version. MongoDB versions follow a major.minor.patch scheme. Minor versions can introduce behavior changes, so always test against your specific patch.
Also check the build info, which includes the storage engine (e.g., wiredTiger) and other details:
db.runCommand({ buildInfo: 1 })
Expected output snippet:
{
version: '6.0.5',
gitVersion: '...',
modules: [],
allocator: 'tcmalloc',
javascriptEngine: 'mozjs',
sysInfo: 'deprecated',
versionArray: [ 6, 0, 5, 0 ],
openssl: { running: 'OpenSSL 1.1.1f 31 Mar 2020', compiled: 'OpenSSL 1.1.1f 31 Mar 2020' },
buildEnvironment: { ... },
bits: 64,
debug: false,
maxBsonObjectSize: 16777216,
storageEngines: [ 'devnull', 'ephemeralForTest', 'wiredTiger' ]
}
Note the maxBsonObjectSize (16 MB by default). This matters when designing documents to avoid exceeding the limit.
Check Deployment Topology
Determine whether you are running standalone, replica set, or sharded cluster:
rs.status()
On a standalone server, this returns an error like not running with --replSet. On a replica set, it shows member states. Capture the topology because many operations (e.g., transactions, change streams) require a replica set.
List Databases and Collections
Read-only observation:
show dbs
show collections
These commands help you understand the data landscape before making changes.
Capture Current Configuration
For replica sets and sharded clusters, capture the current configuration:
rs.conf()
Expected output (snippet):
{
_id: 'myReplicaSet',
version: 5,
term: 3,
members: [
{ _id: 0, host: 'mongodb0.example.net:27017', priority: 1 },
{ _id: 1, host: 'mongodb1.example.net:27017', priority: 1 },
{ _id: 2, host: 'mongodb2.example.net:27017', priority: 0, hidden: true }
],
settings: { ... }
}
Store this in a secure location. It is your rollback reference if a reconfiguration goes wrong.
Prerequisites for Safe Changes
- Read-only access for observation.
- Write access only for intended changes.
- A backup of the configuration files or database before any intervention.
- A recovery plan documented and tested.
Never store real credentials, tokens, or private keys in scripts or articles. Use environment variables or secret management.
Safe Configuration Path
Changing MongoDB configuration should follow a strict path: observe, plan, change one item, verify, and be ready to roll back. This section covers common configuration changes with examples.
Case Study: Enabling Authentication
MongoDB does not enable access control by default in older tutorials, but it is essential in production. Here's the safe path.
Prerequisites
- MongoDB 6.0+ installed.
- Access to the server shell.
- No existing user accounts (fresh install) or ability to create the first admin.
Step 1: Observe Current State
Run:
db.runCommand({ connectionStatus: 1 })
Expected output (snippet):
{
authInfo: { authenticatedUsers: [], authenticatedUserRoles: [] },
ok: 1
}
Empty authenticatedUsers means you are connected without authentication.
Step 2: Plan the Change
You will create a user administrator in the admin database and then restart MongoDB with --auth enabled.
Step 3: Create the Admin User
In mongosh, without authentication:
use admin
db.createUser({
user: "adminUser",
pwd: "ChangeMe123!", // Replace with a strong password, preferably from a secret manager
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
})
Expected output:
{ ok: 1 }
Step 4: Enable Authentication in Config
Edit the MongoDB configuration file (e.g., /etc/mongod.conf) and add:
security:
authorization: enabled
Step 5: Restart MongoDB
Use your init system (e.g., systemctl restart mongod).
Step 6: Verify Authentication is Enforced
Connect without credentials:
mongosh --eval "db.runCommand({ connectionStatus: 1 })"
Expected output should show no authenticated users and possibly an error for operations requiring auth.
Then connect with credentials:
mongosh -u adminUser -p 'ChangeMe123!' --authenticationDatabase admin --eval "db.runCommand({ connectionStatus: 1 })"
Expected output (snippet):
{
authInfo: {
authenticatedUsers: [ { user: 'adminUser', db: 'admin' } ],
authenticatedUserRoles: [ { role: 'userAdminAnyDatabase', db: 'admin' } ]
},
ok: 1
}
Rollback Path
If authentication causes issues (e.g., unable to connect), revert the security.authorization setting to disabled and restart. Then troubleshoot.
Case Study: Adjusting Write Concern
Write concern controls acknowledgment of writes. Changing it can impact durability and performance.
Observe Current Write Concern
db.getWriteConcern()
Expected output (example):
{ w: 'majority', wtimeout: 0 }
Change Write Concern for a Session
db.getMongo().setWriteConcern({ w: "majority", wtimeout: 5000 })
Verify
db.getWriteConcern()
Expected output:
{ w: 'majority', wtimeout: 5000 }
Blast Radius
w: 1returns after primary acknowledgment; faster but less durable.w: "majority"requires acknowledgment from majority of replica set members; stronger durability but potentially slower.wtimeoutspecifies how long to wait for acknowledgment.
Recovery
If writes time out, check replica set health and network. You may need to revert to w: 1 temporarily.
Verification and Diagnostics
After any change, you must verify the outcome. Diagnostics help you detect problems before they become incidents.
Common Diagnostic Commands
Server Status
db.serverStatus()
This returns extensive metrics. Focus on:
connections: current, available, and total created.opcounters: number of operations per second.mem: memory usage.network: bytes in/out.locks: lock acquisition times.wiredTiger: cache and concurrency details.
Example snippet:
{
connections: { current: 15, available: 51185, totalCreated: 120 },
opcounters: { insert: 1000, query: 5000, update: 200, delete: 50, getmore: 100, command: 3000 },
mem: { bits: 64, resident: 500, virtual: 1200, supported: true },
network: { bytesIn: 1000000, bytesOut: 500000, numRequests: 10000 }
}
Current Operations
To see what is running now:
db.currentOp()
Filter for long-running operations:
db.currentOp({ "secs_running": { $gt: 10 } })
This helps identify slow queries or stuck commands.
Profiling
Enable profiling to capture slow queries:
db.setProfilingLevel(1, { slowms: 100 })
This logs operations taking longer than 100 milliseconds to the system.profile collection.
View profile data:
db.system.profile.find().sort({ ts: -1 }).limit(5).pretty()
Replica Set Status
rs.status()
Check health, stateStr (e.g., PRIMARY, SECONDARY), and lastHeartbeat for each member.
Sharded Cluster Status
sh.status()
Shows shards, chunks, and balancer status.
Interpreting Failure Signals
- High
currentOpcount: possible lock contention or slow operations. - Rising
connections.current: connection leak in application. wiredTiger.cacheevictions increasing: cache pressure, consider adding RAM or optimizing queries.- Replica set member
stateStr=RECOVERING: member is catching up, may be due to heavy replication lag. numRequestsspike with no corresponding opcounters: possibly malicious scanning or misconfigured client.
Example: Diagnosing a Slow Query
- Enable profiling:
db.setProfilingLevel(1, { slowms: 50 }). - Wait for slow queries to appear.
- Query
system.profile:
db.system.profile.find({ millis: { $gt: 50 } }).sort({ millis: -1 }).limit(1).pretty()
Expected output (snippet):
{
op: 'query',
ns: 'mydb.orders',
command: {
find: 'orders',
filter: { customer_id: 'C123' },
...
},
keysExamined: 0,
docsExamined: 500000,
nreturned: 10,
millis: 800,
planSummary: 'COLLSCAN'
}
COLLSCAN means a collection scan: no index was used. Fix by creating an index on customer_id.
Failure Modes and Recovery
MongoDB can fail in various ways. Understanding common failure modes and having recovery plans is crucial.
Failure Mode: Primary Node Down (Replica Set)
Symptom: Applications get connection errors or writes fail.
Observation: rs.status() shows the primary as stateStr: 'UNKNOWN' or missing. Secondary members are healthy but cannot elect a primary because they lack majority.
Recovery: If the primary is down and majority of members are still available, an election should occur automatically. Verify with rs.status() that a new primary is elected. If not, check network partitions and member configurations. You may need to force reconfiguration if majority is lost (use with extreme caution).
Example Force Reconfiguration (only when necessary):
cfg = rs.conf()
// Remove unreachable member from voting members
cfg.members = cfg.members.filter(m => m.host !== 'down-node:27017')
cfg.version += 1
rs.reconfig(cfg, { force: true })
Verification: rs.status() should show a new primary and remaining members healthy.
Failure Mode: Replication Lag
Symptom: Secondary members lag behind primary, causing reads to return stale data.
Observation: rs.printReplicationInfo() and rs.printSecondaryReplicationInfo() show lag in seconds.
Recovery: Identify cause: network latency, heavy write load, secondary hardware insufficient, or long-running operations on secondary. Optimize writes, add capacity, or reduce secondary read load.
Verification: Lag decreases below acceptable threshold (e.g., <2 seconds for your use case).
Failure Mode: Disk Full
Symptom: Writes fail with QuotaExceeded or No space left on device.
Observation: Check disk usage: df -h on server.
Recovery: Free space by removing old backups, logs, or moving data files to larger disk. For immediate relief, consider shrinking the oplog if it is large (but understand implications). Long-term: monitor disk usage and set alerts.
Verification: Writes succeed and disk usage is below threshold.
Failure Mode: Authentication Issues
Symptom: Applications cannot connect with Authentication failed errors.
Observation: Check user accounts and roles: db.getUsers() and db.getRoles().
Recovery: If user password is lost, reset it. If authentication mechanism is wrong (e.g., SCRAM vs x.509), correct the connection string. Ensure the authentication database is specified.
Example Password Reset:
db.changeUserPassword("appUser", "NewStrongPass123!")
Verification: Test connection with new password.
Best Practices for Recovery
- Always have recent backups and test restore procedures.
- Use
mongodumpandmongorestoreregularly. - Document recovery runbooks.
- Simulate failures in staging to practice recovery.
Operations Checklist
A daily operations checklist ensures consistent monitoring and quick issue detection. Below is a concrete checklist with commands and expected outcomes.
Daily Operations Checklist
| Item | How to Check | Expected Result | If Failure |
|---|---|---|---|
| Replica set health | rs.status() | All members health: 1, one PRIMARY | Investigate member state, check logs |
| Replication lag | rs.printSecondaryReplicationInfo() | Lag less than threshold (e.g., 5 seconds) | Scale secondary, optimize writes |
| Disk usage | df -h on data directory | Usage below 80% | Clean up, expand storage |
| Connection count | db.serverStatus().connections.current | Below max configured (e.g., 70% of maxIncomingConnections) | Check app connection leaks, scale horizontally |
| Slow queries | Query system.profile for queries >100ms | No new slow queries | Optimize indexes, rewrite queries |
| Oplog size | rs.printReplicationInfo() | Oplog time window >24 hours | Increase oplog size if needed |
| Error logs | Check MongoDB log file for E lines | No new errors | Investigate and resolve root cause |
Weekly Maintenance Tasks
- Review indexes:
db.collection.getIndexes()for unused or redundant indexes. - Check for fragmented storage (WiredTiger compact):
db.runCommand({ compact: 'collectionName' })(careful: blocks operations). - Backup verification: restore latest backup to test instance.
- Review security: user roles, authentication settings.
Monthly Review
- Evaluate performance trends: use
mongostatandmongotopsnapshots. - Plan capacity based on data growth.
- Test disaster recovery procedures.
Single Accountable Owner
For each major decision in operations (e.g., enabling authentication, changing write concern, performing maintenance), designate a single accountable owner. For example:
- Authentication change: Priya Shah, Database Administrator. Revisit if security audit dictates.
- Write concern adjustment: Tom Chen, Lead Backend Engineer. Review quarterly.
- Backup verification: Maria Gomez, DevOps Engineer. Monthly verification.
This avoids diffusion of responsibility and ensures someone is always watching.
Common Pitfalls and How to Avoid Them
In working with MongoDB, teams often make mistakes that lead to downtime or data loss. Here are the most common pitfalls and how to avoid or recover from them.
1. Running Without Authentication on Public Interfaces
Why it happens: Default configuration or quick setup for development, then deployed to production without hardening.
Impact: Database is open to anyone, leading to data theft or ransomware.
How to avoid: Always enable authentication before exposing MongoDB to any network. Use security.authorization: enabled and create users with least privilege.
Recovery: If discovered, immediately enable authentication and rotate credentials. Audit logs for unauthorized access.
2. Ignoring Replication Lag
Why it happens: Lag is not monitored or alerts not configured.
Impact: Secondary reads return stale data, and failover may result in data loss if lag is high.
How to avoid: Monitor lag continuously with tools like Prometheus + MongoDB exporter. Set alerts when lag exceeds threshold.
Recovery: Investigate cause: network, write patterns, secondary load. Optimize or scale.
3. Not Testing Backups
Why it happens: Assumption that backups work because they run.
Impact: In a disaster, backup restore fails, causing permanent data loss.
How to avoid: Regularly restore backups to a test environment and verify data integrity. Automate this process.
Recovery: If backup is corrupt, use point-in-time recovery from oplog if available. Otherwise, attempt salvage tools. Prevention is key.
4. Overloading the Primary with Read Operations
Why it happens: Application reads go to primary because connection string is not configured for secondary reads.
Impact: Primary performance degrades, affecting writes and causing cascading failures.
How to avoid: Use readPreference=secondaryPreferred in connection string to distribute reads.
Recovery: Modify connection string and redeploy application. Monitor primary CPU and connections.
5. Misconfiguring Write Concern
Why it happens: Default write concern may be w:1, and developers think data is safely written when it is not.
Impact: Data loss if primary fails before replication.
How to avoid: Use w: "majority" for critical writes. Test failover scenarios.
Recovery: If data loss occurs, restore from backup or oplog if possible.
6. Not Upgrading in a Controlled Manner
Why it happens: Upgrade without testing compatibility or reading release notes.
Impact: Driver incompatibility, deprecated features, unexpected behavior changes.
How to avoid: Test upgrades in staging with the same data and workload. Follow MongoDB upgrade path (cannot jump major versions).
Recovery: Rollback to previous version if possible, and fix issues before retrying.
Conclusion
MongoDB advanced operations demand a systematic approach: know your version and topology, change configuration carefully, verify with diagnostics, prepare for failures, and follow a daily operations checklist. Each recommendation in this guide is version-scoped and observable, with explicit recovery paths.
As a next step, start with a low-risk verification: run db.version(), rs.status(), and db.serverStatus() to establish a baseline. Document the current state, then implement monitoring for replication lag and slow queries. Assign a single owner for each major operational area.
Remember: a reliable workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. By applying these advanced concepts, you can operate MongoDB with confidence.